TypeScript 的兩種聲明文件寫法的區別和根本意義

做者: Angus.Fenying <i.am.x.fenying@gmail.com>node

日期: 2016-09-15 05:40 PM瀏覽器

咱們知道 NPM 包能夠有內建的 TS 聲明文件,從而免去使用 typings 工具安裝 TS 聲明文件的操做。那既然能夠有內建的聲明文件,爲什麼還須要額外安裝呢?由於不是 全部人都在使用 TypeScript,不少 NPM 模塊都是純 JavaScript 編寫的,其做者 沒有太大的可能性爲之編寫模塊聲明文件。並且內建的聲明文件有必定約束。app

TypeScript 的 DefinitelyTyped 聲明文件有兩種寫法,一種叫作 全局類型聲明(Global Type Definition),另外一個則是叫作 模塊導出聲明(External Module Definition)函數

External Module 一詞源自 TypeScript 1.5 以前的 內部模塊/外部模塊 之說,而 1.5 以後內部模塊變成了 namespace,外部模塊直接化爲模塊, 再也不有內外部模塊之說。工具

因此這裏將 External Module Definition 譯爲 模塊導出聲明 比較合適。ui

DefinitelyTyped 對此的說明是:spa

  • 若是你的模塊須要將新的名稱引入全局命名空間,那麼就應該使用全局聲明。
  • 若是你的模塊無需將新的名稱引入全局命名空間,那麼就應該使用模塊導出聲明。

這二者有什麼不一樣呢?code

首先,最主要的區別在於,NPM 模塊裏面的內建聲明文件必須使用模塊導出聲明寫法, 不然 TypeScript 編譯沒法經過。下面來看一個簡單的例子:ip

以一個名爲 abc 的模塊爲例,TS源代碼以下:get

export function abc(s: string): string {

    return s.substr(0, 4);
}

其聲明文件有兩種寫法:

  • 第一種,模塊導出聲明寫法

    declare interface funcAbcSign {
        (s: string): string
    }
    
    export declare let abc: funcAbcSign;
  • 第二種,全局類型聲明寫法

    declare module "abc" {
        interface funcAbcSign {
            (s: string): string
        }
    
        export let abc: funcAbcSign;
    }

若是要理解二者的區別,首先要理解其意義。模塊導出聲明寫法 中,單從這個文件內容 看來是沒法得知這些內容屬於哪一個模塊的。因此必須將之與模塊放在一塊兒,做爲內建聲明文件, TypeScript 編譯器才能得知其所屬的模塊。(或者放進 typings 的 external 目錄)

全局類型聲明寫法 中,其實是將模塊名稱 abc 引入了全局空間,即告訴 TypeScript 編譯器,存在一個叫 abc 的模塊,想使用裏面的名稱,就 import 吧!

由於任何一個 Node.js 的模塊都是必須依靠 require 加載的才能經過字段引用的方式 使用裏面的名稱,便是說 一個模塊沒法真正地將任何名稱引入全局的命名空間中,因此不該該也不能在一個模塊 的內建聲明文件裏使用全局聲明寫法!

這樣,就明白了何時應該使用全局聲明寫法,何時應該使用模塊導出聲明寫法了

若是你寫的 TypeScript 文件是在瀏覽器上經過 <script> 標籤加載的,那麼裏面的 變量、函數等都會被引入到全局命名空間中,這時候就須要爲這個文件寫一份全局類型聲明 了!好比這個文件:

// 定義了一個全局變量,整個頁面內均可見
let appStartedTime: Date = new Date();

你須要爲之寫一份 .d.ts 文件,供其它 ts 文件引用:

// 這裏將變量名 appStartedTime 引入了 TypeScript 的全局命名空間中。
declare let appStartedTime: Date;

除此以外,你能夠用一個全局類型聲明文件定義多個模塊、命名空間,例如:

// node.d.ts

declare namespace NodeJS {

    export interface Error {

        "name": string;
    }
}

declare module "http" {

    // Node built-in module http
}

declare module "fs" {

    // Node built-in module fs
}

或者深層模塊聲明:

declare module "sample/lib1" {

    export let name: number;
}

declare module "sample/lib2" {

    export let value: number;
}

declare module "sample" {

    export * from "sample/lib1";

    export * from "sample/lib2";
}

這是模塊導出聲明寫法作不到的。

綜上所述,最直觀的解釋是:

  • 全局類型聲明裏的名稱將被引入整個 TypeScript 全局命名空間中,從引用這個 聲明文件起就能夠自由使用。
  • 模塊導出聲明裏的名稱必須經過 import/require 才能使用。
相關文章
相關標籤/搜索