中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

黑河商城網(wǎng)站建設(shè)/東莞網(wǎng)絡(luò)推廣平臺

黑河商城網(wǎng)站建設(shè),東莞網(wǎng)絡(luò)推廣平臺,網(wǎng)站開發(fā)方案設(shè)計,頁面跳轉(zhuǎn) 英文declare var 聲明全局變量declare function 聲明全局方法declare class 聲明全局類declare enum 聲明全局枚舉類型declare namespace 聲明(含有子屬性的)全局對象interface 和 type 聲明全局類型export 導(dǎo)出變量export namespace 導(dǎo)出(含有子…
  • declare var?聲明全局變量
  • declare function?聲明全局方法
  • declare class?聲明全局類
  • declare enum?聲明全局枚舉類型
  • declare namespace?聲明(含有子屬性的)全局對象
  • interface?和?type?聲明全局類型
  • export?導(dǎo)出變量
  • export namespace?導(dǎo)出(含有子屬性的)對象
  • export default?ES6 默認導(dǎo)出
  • export =?commonjs 導(dǎo)出模塊
  • export as namespace?UMD 庫聲明全局變量
  • declare global?擴展全局變量
  • declare module?擴展模塊
  • /// <reference />?三斜線指令

什么是聲明語句§

假如我們想使用第三方庫 jQuery,一種常見的方式是在 html 中通過?<script>?標簽引入 jQuery,然后就可以使用全局變量?$?或?jQuery?了。

我們通常這樣獲取一個?id?是?foo?的元素:

$('#foo');
// or
jQuery('#foo');

但是在 ts 中,編譯器并不知道?$?或?jQuery?是什么東西1:

jQuery('#foo');
// ERROR: Cannot find name 'jQuery'.

這時,我們需要使用?declare var?來定義它的類型2:

declare var jQuery: (selector: string) => any;jQuery('#foo');

上例中,declare var?并沒有真的定義一個變量,只是定義了全局變量?jQuery?的類型,僅僅會用于編譯時的檢查,在編譯結(jié)果中會被刪除。它編譯結(jié)果是:

jQuery('#foo');

除了?declare var?之外,還有其他很多種聲明語句,將會在后面詳細介紹。

什么是聲明文件§

通常我們會把聲明語句放到一個單獨的文件(jQuery.d.ts)中,這就是聲明文件3:

// src/jQuery.d.tsdeclare var jQuery: (selector: string) => any;
// src/index.tsjQuery('#foo');

聲明文件必需以?.d.ts?為后綴。

一般來說,ts 會解析項目中所有的?*.ts?文件,當然也包含以?.d.ts?結(jié)尾的文件。所以當我們將?jQuery.d.ts?放到項目中時,其他所有?*.ts?文件就都可以獲得?jQuery?的類型定義了。

/path/to/project
├── src
|  ├── index.ts
|  └── jQuery.d.ts
└── tsconfig.json

假如仍然無法解析,那么可以檢查下?tsconfig.json?中的?files、include?和?exclude?配置,確保其包含了?jQuery.d.ts?文件。

這里只演示了全局變量這種模式的聲明文件,假如是通過模塊導(dǎo)入的方式使用第三方庫的話,那么引入聲明文件又是另一種方式了,將會在后面詳細介紹。

第三方聲明文件§

當然,jQuery 的聲明文件不需要我們定義了,社區(qū)已經(jīng)幫我們定義好了:jQuery in DefinitelyTyped。

我們可以直接下載下來使用,但是更推薦的是使用?@types?統(tǒng)一管理第三方庫的聲明文件。

@types?的使用方式很簡單,直接用 npm 安裝對應(yīng)的聲明模塊即可,以 jQuery 舉例:

npm install @types/jquery --save-dev

可以在這個頁面搜索你需要的聲明文件。

書寫聲明文件§

當一個第三方庫沒有提供聲明文件時,我們就需要自己書寫聲明文件了。前面只介紹了最簡單的聲明文件內(nèi)容,而真正書寫一個聲明文件并不是一件簡單的事,以下會詳細介紹如何書寫聲明文件。

在不同的場景下,聲明文件的內(nèi)容和使用方式會有所區(qū)別。

庫的使用場景主要有以下幾種:

  • 全局變量:通過?<script>?標簽引入第三方庫,注入全局變量
  • npm 包:通過?import foo from 'foo'?導(dǎo)入,符合 ES6 模塊規(guī)范
  • UMD 庫:既可以通過?<script>?標簽引入,又可以通過?import?導(dǎo)入
  • 直接擴展全局變量:通過?<script>?標簽引入后,改變一個全局變量的結(jié)構(gòu)
  • 在 npm 包或 UMD 庫中擴展全局變量:引用 npm 包或 UMD 庫后,改變一個全局變量的結(jié)構(gòu)
  • 模塊插件:通過?<script>?或?import?導(dǎo)入后,改變另一個模塊的結(jié)構(gòu)

全局變量§

全局變量是最簡單的一種場景,之前舉的例子就是通過?<script>?標簽引入 jQuery,注入全局變量?$?和?jQuery。

使用全局變量的聲明文件時,如果是以?npm install @types/xxx --save-dev?安裝的,則不需要任何配置。如果是將聲明文件直接存放于當前項目中,則建議和其他源碼一起放到?src?目錄下(或者對應(yīng)的源碼目錄下):

/path/to/project
├── src
|  ├── index.ts
|  └── jQuery.d.ts
└── tsconfig.json

如果沒有生效,可以檢查下?tsconfig.json?中的?files、include?和?exclude?配置,確保其包含了?jQuery.d.ts?文件。

全局變量的聲明文件主要有以下幾種語法:

  • declare var?聲明全局變量
  • declare function?聲明全局方法
  • declare class?聲明全局類
  • declare enum?聲明全局枚舉類型
  • declare namespace?聲明(含有子屬性的)全局對象
  • interface?和?type?聲明全局類型

declare var§

在所有的聲明語句中,declare var?是最簡單的,如之前所學(xué),它能夠用來定義一個全局變量的類型。與其類似的,還有?declare let?和?declare const,使用?let?與使用?var?沒有什么區(qū)別:

// src/jQuery.d.tsdeclare let jQuery: (selector: string) => any;
// src/index.tsjQuery('#foo');
// 使用 declare let 定義的 jQuery 類型,允許修改這個全局變量
jQuery = function(selector) {return document.querySelector(selector);
};

而當我們使用?const?定義時,表示此時的全局變量是一個常量,不允許再去修改它的值了4:

// src/jQuery.d.tsdeclare const jQuery: (selector: string) => any;jQuery('#foo');
// 使用 declare const 定義的 jQuery 類型,禁止修改這個全局變量
jQuery = function(selector) {return document.querySelector(selector);
};
// ERROR: Cannot assign to 'jQuery' because it is a constant or a read-only property.

一般來說,全局變量都是禁止修改的常量,所以大部分情況都應(yīng)該使用?const?而不是?var?或?let。

需要注意的是,聲明語句中只能定義類型,切勿在聲明語句中定義具體的實現(xiàn)5:

declare const jQuery = function(selector) {return document.querySelector(selector);
};
// ERROR: An implementation cannot be declared in ambient contexts.

declare function§

declare function?用來定義全局函數(shù)的類型。jQuery 其實就是一個函數(shù),所以也可以用?function?來定義:

// src/jQuery.d.tsdeclare function jQuery(selector: string): any;
// src/index.tsjQuery('#foo');

在函數(shù)類型的聲明語句中,函數(shù)重載也是支持的6:

// src/jQuery.d.tsdeclare function jQuery(selector: string): any;
declare function jQuery(domReadyCallback: () => any): any;
// src/index.tsjQuery('#foo');
jQuery(function() {alert('Dom Ready!');
});

declare class§

當全局變量是一個類的時候,我們用?declare class?來定義它的類型7:

// src/Animal.d.tsdeclare class Animal {name: string;constructor(name: string);sayHi(): string;
}
// src/index.tslet cat = new Animal('Tom');

同樣的,declare class?語句也只能用來定義類型,不能用來定義具體的實現(xiàn),比如定義?sayHi?方法的具體實現(xiàn)則會報錯:

// src/Animal.d.tsdeclare class Animal {name: string;constructor(name: string);sayHi() {return `My name is ${this.name}`;};// ERROR: An implementation cannot be declared in ambient contexts.
}

declare enum§

使用?declare enum?定義的枚舉類型也稱作外部枚舉(Ambient Enums),舉例如下8:

// src/Directions.d.tsdeclare enum Directions {Up,Down,Left,Right
}
// src/index.tslet directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

與其他全局變量的類型聲明一致,declare enum?僅用來定義類型,而不是具體的值。

Directions.d.ts?僅僅會用于編譯時的檢查,聲明文件里的內(nèi)容在編譯結(jié)果中會被刪除。它編譯結(jié)果是:

var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

其中?Directions?是由第三方庫定義好的全局變量。

declare namespace§

namespace?是 ts 早期時為了解決模塊化而創(chuàng)造的關(guān)鍵字,中文稱為命名空間。

由于歷史遺留原因,在早期還沒有 ES6 的時候,ts 提供了一種模塊化方案,使用?module?關(guān)鍵字表示內(nèi)部模塊。但由于后來 ES6 也使用了?module?關(guān)鍵字,ts 為了兼容 ES6,使用?namespace?替代了自己的?module,更名為命名空間。

隨著 ES6 的廣泛應(yīng)用,現(xiàn)在已經(jīng)不建議再使用 ts 中的?namespace,而推薦使用 ES6 的模塊化方案了,故我們不再需要學(xué)習(xí)?namespace?的使用了。

namespace?被淘汰了,但是在聲明文件中,declare namespace?還是比較常用的,它用來表示全局變量是一個對象,包含很多子屬性。

比如?jQuery?是一個全局變量,它是一個對象,提供了一個?jQuery.ajax?方法可以調(diào)用,那么我們就應(yīng)該使用?declare namespace jQuery?來聲明這個擁有多個子屬性的全局變量。

// src/jQuery.d.tsdeclare namespace jQuery {function ajax(url: string, settings?: any): void;
}
// src/index.tsjQuery.ajax('/api/get_something');

注意,在?declare namespace?內(nèi)部,我們直接使用?function ajax?來聲明函數(shù),而不是使用?declare function ajax。類似的,也可以使用?const,?class,?enum?等語句9:

// src/jQuery.d.tsdeclare namespace jQuery {function ajax(url: string, settings?: any): void;const version: number;class Event {blur(eventType: EventType): void}enum EventType {CustomClick}
}
// src/index.tsjQuery.ajax('/api/get_something');
console.log(jQuery.version);
const e = new jQuery.Event();
e.blur(jQuery.EventType.CustomClick);

嵌套的命名空間§

如果對象擁有深層的層級,則需要用嵌套的?namespace?來聲明深層的屬性的類型10:

// src/jQuery.d.tsdeclare namespace jQuery {function ajax(url: string, settings?: any): void;namespace fn {function extend(object: any): void;}
}
// src/index.tsjQuery.ajax('/api/get_something');
jQuery.fn.extend({check: function() {return this.each(function() {this.checked = true;});}
});

假如?jQuery?下僅有?fn?這一個屬性(沒有?ajax?等其他屬性或方法),則可以不需要嵌套?namespace11:

// src/jQuery.d.tsdeclare namespace jQuery.fn {function extend(object: any): void;
}
// src/index.tsjQuery.fn.extend({check: function() {return this.each(function() {this.checked = true;});}
});

interface?和?type§

除了全局變量之外,可能有一些類型我們也希望能暴露出來。在類型聲明文件中,我們可以直接使用?interface?或?type?來聲明一個全局的接口或類型12:

// src/jQuery.d.tsinterface AjaxSettings {method?: 'GET' | 'POST'data?: any;
}
declare namespace jQuery {function ajax(url: string, settings?: AjaxSettings): void;
}

這樣的話,在其他文件中也可以使用這個接口或類型了:

// src/index.tslet settings: AjaxSettings = {method: 'POST',data: {name: 'foo'}
};
jQuery.ajax('/api/post_something', settings);

type?與?interface?類似,不再贅述。

防止命名沖突§

暴露在最外層的?interface?或?type?會作為全局類型作用于整個項目中,我們應(yīng)該盡可能的減少全局變量或全局類型的數(shù)量。故最好將他們放到?namespace?下13:

// src/jQuery.d.tsdeclare namespace jQuery {interface AjaxSettings {method?: 'GET' | 'POST'data?: any;}function ajax(url: string, settings?: AjaxSettings): void;
}

注意,在使用這個?interface?的時候,也應(yīng)該加上?jQuery?前綴:

// src/index.tslet settings: jQuery.AjaxSettings = {method: 'POST',data: {name: 'foo'}
};
jQuery.ajax('/api/post_something', settings);

聲明合并§

假如 jQuery 既是一個函數(shù),可以直接被調(diào)用?jQuery('#foo'),又是一個對象,擁有子屬性?jQuery.ajax()(事實確實如此),那么我們可以組合多個聲明語句,它們會不沖突的合并起來14:

// src/jQuery.d.tsdeclare function jQuery(selector: string): any;
declare namespace jQuery {function ajax(url: string, settings?: any): void;
}
// src/index.tsjQuery('#foo');
jQuery.ajax('/api/get_something');

關(guān)于聲明合并的更多用法,可以查看聲明合并章節(jié)。

npm 包§

一般我們通過?import foo from 'foo'?導(dǎo)入一個 npm 包,這是符合 ES6 模塊規(guī)范的。

在我們嘗試給一個 npm 包創(chuàng)建聲明文件之前,需要先看看它的聲明文件是否已經(jīng)存在。一般來說,npm 包的聲明文件可能存在于兩個地方:

  1. 與該 npm 包綁定在一起。判斷依據(jù)是?package.json?中有?types?字段,或者有一個?index.d.ts?聲明文件。這種模式不需要額外安裝其他包,是最為推薦的,所以以后我們自己創(chuàng)建 npm 包的時候,最好也將聲明文件與 npm 包綁定在一起。
  2. 發(fā)布到?@types?里。我們只需要嘗試安裝一下對應(yīng)的?@types?包就知道是否存在該聲明文件,安裝命令是?npm install @types/foo --save-dev。這種模式一般是由于 npm 包的維護者沒有提供聲明文件,所以只能由其他人將聲明文件發(fā)布到?@types?里了。

假如以上兩種方式都沒有找到對應(yīng)的聲明文件,那么我們就需要自己為它寫聲明文件了。由于是通過?import?語句導(dǎo)入的模塊,所以聲明文件存放的位置也有所約束,一般有兩種方案:

  1. 創(chuàng)建一個?node_modules/@types/foo/index.d.ts?文件,存放?foo?模塊的聲明文件。這種方式不需要額外的配置,但是?node_modules?目錄不穩(wěn)定,代碼也沒有被保存到倉庫中,無法回溯版本,有不小心被刪除的風(fēng)險,故不太建議用這種方案,一般只用作臨時測試。
  2. 創(chuàng)建一個?types?目錄,專門用來管理自己寫的聲明文件,將?foo?的聲明文件放到?types/foo/index.d.ts?中。這種方式需要配置下?tsconfig.json?中的?paths?和?baseUrl?字段。

目錄結(jié)構(gòu):

/path/to/project
├── src
|  └── index.ts
├── types
|  └── foo
|     └── index.d.ts
└── tsconfig.json

tsconfig.json?內(nèi)容:

{"compilerOptions": {"module": "commonjs","baseUrl": "./","paths": {"*": ["types/*"]}}
}

如此配置之后,通過?import?導(dǎo)入?foo?的時候,也會去?types?目錄下尋找對應(yīng)的模塊的聲明文件了。

注意?module?配置可以有很多種選項,不同的選項會影響模塊的導(dǎo)入導(dǎo)出模式。這里我們使用了?commonjs?這個最常用的選項,后面的教程也都默認使用的這個選項。

不管采用了以上兩種方式中的哪一種,我都強烈建議大家將書寫好的聲明文件(通過給第三方庫發(fā) pull request,或者直接提交到?@types?里)發(fā)布到開源社區(qū)中,享受了這么多社區(qū)的優(yōu)秀的資源,就應(yīng)該在力所能及的時候給出一些回饋。只有所有人都參與進來,才能讓 ts 社區(qū)更加繁榮。

npm 包的聲明文件主要有以下幾種語法:

  • export?導(dǎo)出變量
  • export namespace?導(dǎo)出(含有子屬性的)對象
  • export default?ES6 默認導(dǎo)出
  • export =?commonjs 導(dǎo)出模塊

export§

npm 包的聲明文件與全局變量的聲明文件有很大區(qū)別。在 npm 包的聲明文件中,使用?declare?不再會聲明一個全局變量,而只會在當前文件中聲明一個局部變量。只有在聲明文件中使用?export?導(dǎo)出,然后在使用方?import?導(dǎo)入后,才會應(yīng)用到這些類型聲明。

export?的語法與普通的 ts 中的語法類似,區(qū)別僅在于聲明文件中禁止定義具體的實現(xiàn)15:

// types/foo/index.d.tsexport const name: string;
export function getName(): string;
export class Animal {constructor(name: string);sayHi(): string;
}
export enum Directions {Up,Down,Left,Right
}
export interface Options {data: any;
}

對應(yīng)的導(dǎo)入和使用模塊應(yīng)該是這樣:

// src/index.tsimport { name, getName, Animal, Directions, Options } from 'foo';console.log(name);
let myName = getName();
let cat = new Animal('Tom');
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
let options: Options = {data: {name: 'foo'}
};

混用?declare?和?export§

我們也可以使用?declare?先聲明多個變量,最后再用?export?一次性導(dǎo)出。上例的聲明文件可以等價的改寫為16:

// types/foo/index.d.tsdeclare const name: string;
declare function getName(): string;
declare class Animal {constructor(name: string);sayHi(): string;
}
declare enum Directions {Up,Down,Left,Right
}
interface Options {data: any;
}export { name, getName, Animal, Directions, Options };

注意,與全局變量的聲明文件類似,interface?前是不需要?declare?的。

export namespace§

與?declare namespace?類似,export namespace?用來導(dǎo)出一個擁有子屬性的對象17:

// types/foo/index.d.tsexport namespace foo {const name: string;namespace bar {function baz(): string;}
}
// src/index.tsimport { foo } from 'foo';console.log(foo.name);
foo.bar.baz();

export default§

在 ES6 模塊系統(tǒng)中,使用?export default?可以導(dǎo)出一個默認值,使用方可以用?import foo from 'foo'?而不是?import { foo } from 'foo'?來導(dǎo)入這個默認值。

在類型聲明文件中,export default?用來導(dǎo)出默認值的類型18:

// types/foo/index.d.tsexport default function foo(): string;
// src/index.tsimport foo from 'foo';foo();

注意,只有?function、class?和?interface?可以直接默認導(dǎo)出,其他的變量需要先定義出來,再默認導(dǎo)出19:

// types/foo/index.d.tsexport default enum Directions {
// ERROR: Expression expected.Up,Down,Left,Right
}

上例中?export default enum?是錯誤的語法,需要使用?declare enum?定義出來,然后使用?export default?導(dǎo)出:

// types/foo/index.d.tsdeclare enum Directions {Up,Down,Left,Right
}export default Directions;

針對這種默認導(dǎo)出,我們一般會將導(dǎo)出語句放在整個聲明文件的最前面20:

// types/foo/index.d.tsexport default Directions;declare enum Directions {Up,Down,Left,Right
}

export =§

在 commonjs 規(guī)范中,我們用以下方式來導(dǎo)出一個模塊:

// 整體導(dǎo)出
module.exports = foo;
// 單個導(dǎo)出
exports.bar = bar;

在 ts 中,針對這種模塊導(dǎo)出,有多種方式可以導(dǎo)入,第一種方式是?const ... = require

// 整體導(dǎo)入
const foo = require('foo');
// 單個導(dǎo)入
const bar = require('foo').bar;

第二種方式是?import ... from,注意針對整體導(dǎo)出,需要使用?import * as?來導(dǎo)入:

// 整體導(dǎo)入
import * as foo from 'foo';
// 單個導(dǎo)入
import { bar } from 'foo';

第三種方式是?import ... require,這也是 ts 官方推薦的方式:

// 整體導(dǎo)入
import foo = require('foo');
// 單個導(dǎo)入
import bar = foo.bar;

對于這種使用 commonjs 規(guī)范的庫,假如要為它寫類型聲明文件的話,就需要使用到?export =?這種語法了21:

// types/foo/index.d.tsexport = foo;declare function foo(): string;
declare namespace foo {const bar: number;
}

需要注意的是,上例中使用了?export =?之后,就不能再單個導(dǎo)出?export { bar }?了。所以我們通過聲明合并,使用?declare namespace foo?來將?bar?合并到?foo?里。

準確地講,export =?不僅可以用在聲明文件中,也可以用在普通的 ts 文件中。實際上,import ... require?和?export =?都是 ts 為了兼容 AMD 規(guī)范和 commonjs 規(guī)范而創(chuàng)立的新語法,由于并不常用也不推薦使用,所以這里就不詳細介紹了,感興趣的可以看官方文檔。

由于很多第三方庫是 commonjs 規(guī)范的,所以聲明文件也就不得不用到?export =?這種語法了。但是還是需要再強調(diào)下,相比與?export =,我們更推薦使用 ES6 標準的?export default?和?export。

UMD 庫§

既可以通過?<script>?標簽引入,又可以通過?import?導(dǎo)入的庫,稱為 UMD 庫。相比于 npm 包的類型聲明文件,我們需要額外聲明一個全局變量,為了實現(xiàn)這種方式,ts 提供了一個新語法?export as namespace。

export as namespace§

一般使用?export as namespace?時,都是先有了 npm 包的聲明文件,再基于它添加一條?export as namespace?語句,即可將聲明好的一個變量聲明為全局變量,舉例如下22:

// types/foo/index.d.tsexport as namespace foo;
export = foo;declare function foo(): string;
declare namespace foo {const bar: number;
}

當然它也可以與?export default?一起使用:

// types/foo/index.d.tsexport as namespace foo;
export default foo;declare function foo(): string;
declare namespace foo {const bar: number;
}

直接擴展全局變量§

有的第三方庫擴展了一個全局變量,可是此全局變量的類型卻沒有相應(yīng)的更新過來,就會導(dǎo)致 ts 編譯錯誤,此時就需要擴展全局變量的類型。比如擴展?String?類型23:

interface String {prependHello(): string;
}'foo'.prependHello();

通過聲明合并,使用?interface String?即可給?String?添加屬性或方法。

也可以使用?declare namespace?給已有的命名空間添加類型聲明24:

// types/jquery-plugin/index.d.tsdeclare namespace JQuery {interface CustomOptions {bar: string;}
}interface JQueryStatic {foo(options: JQuery.CustomOptions): string;
}
// src/index.tsjQuery.foo({bar: ''
});

在 npm 包或 UMD 庫中擴展全局變量§

如之前所說,對于一個 npm 包或者 UMD 庫的聲明文件,只有?export?導(dǎo)出的類型聲明才能被導(dǎo)入。所以對于 npm 包或 UMD 庫,如果導(dǎo)入此庫之后會擴展全局變量,則需要使用另一種語法在聲明文件中擴展全局變量的類型,那就是?declare global。

declare global§

使用?declare global?可以在 npm 包或者 UMD 庫的聲明文件中擴展全局變量的類型25:

// types/foo/index.d.tsdeclare global {interface String {prependHello(): string;}
}export {};
// src/index.ts'bar'.prependHello();

注意即使此聲明文件不需要導(dǎo)出任何東西,仍然需要導(dǎo)出一個空對象,用來告訴編譯器這是一個模塊的聲明文件,而不是一個全局變量的聲明文件。

模塊插件§

有時通過?import?導(dǎo)入一個模塊插件,可以改變另一個原有模塊的結(jié)構(gòu)。此時如果原有模塊已經(jīng)有了類型聲明文件,而插件模塊沒有類型聲明文件,就會導(dǎo)致類型不完整,缺少插件部分的類型。ts 提供了一個語法?declare module,它可以用來擴展原有模塊的類型。

declare module§

如果是需要擴展原有模塊的話,需要在類型聲明文件中先引用原有模塊,再使用?declare module?擴展原有模塊26:

// types/moment-plugin/index.d.tsimport * as moment from 'moment';declare module 'moment' {export function foo(): moment.CalendarKey;
}
// src/index.tsimport * as moment from 'moment';
import 'moment-plugin';moment.foo();

declare module?也可用于在一個文件中一次性聲明多個模塊的類型27:

// types/foo-bar.d.tsdeclare module 'foo' {export interface Foo {foo: string;}
}declare module 'bar' {export function bar(): string;
}
// src/index.tsimport { Foo } from 'foo';
import * as bar from 'bar';let f: Foo;
bar.bar();

聲明文件中的依賴§

一個聲明文件有時會依賴另一個聲明文件中的類型,比如在前面的?declare module?的例子中,我們就在聲明文件中導(dǎo)入了?moment,并且使用了?moment.CalendarKey?這個類型:

// types/moment-plugin/index.d.tsimport * as moment from 'moment';declare module 'moment' {export function foo(): moment.CalendarKey;
}

除了可以在聲明文件中通過?import?導(dǎo)入另一個聲明文件中的類型之外,還有一個語法也可以用來導(dǎo)入另一個聲明文件,那就是三斜線指令。

三斜線指令§

與?namespace?類似,三斜線指令也是 ts 在早期版本中為了描述模塊之間的依賴關(guān)系而創(chuàng)造的語法。隨著 ES6 的廣泛應(yīng)用,現(xiàn)在已經(jīng)不建議再使用 ts 中的三斜線指令來聲明模塊之間的依賴關(guān)系了。

但是在聲明文件中,它還是有一定的用武之地。

類似于聲明文件中的?import,它可以用來導(dǎo)入另一個聲明文件。與?import?的區(qū)別是,當且僅當在以下幾個場景下,我們才需要使用三斜線指令替代?import

  • 當我們在書寫一個全局變量的聲明文件時
  • 當我們需要依賴一個全局變量的聲明文件時

書寫一個全局變量的聲明文件§

這些場景聽上去很拗口,但實際上很好理解——在全局變量的聲明文件中,是不允許出現(xiàn)?import,?export?關(guān)鍵字的。一旦出現(xiàn)了,那么他就會被視為一個 npm 包或 UMD 庫,就不再是全局變量的聲明文件了。故當我們在書寫一個全局變量的聲明文件時,如果需要引用另一個庫的類型,那么就必須用三斜線指令了28:

// types/jquery-plugin/index.d.ts/// <reference types="jquery" />declare function foo(options: JQuery.AjaxSettings): string;
// src/index.tsfoo({});

三斜線指令的語法如上,///?后面使用 xml 的格式添加了對?jquery?類型的依賴,這樣就可以在聲明文件中使用?JQuery.AjaxSettings?類型了。

注意,三斜線指令必須放在文件的最頂端,三斜線指令的前面只允許出現(xiàn)單行或多行注釋。

依賴一個全局變量的聲明文件§

在另一個場景下,當我們需要依賴一個全局變量的聲明文件時,由于全局變量不支持通過?import?導(dǎo)入,當然也就必須使用三斜線指令來引入了29:

// types/node-plugin/index.d.ts/// <reference types="node" />export function foo(p: NodeJS.Process): string;
// src/index.tsimport { foo } from 'node-plugin';foo(global.process);

在上面的例子中,我們通過三斜線指引入了?node?的類型,然后在聲明文件中使用了?NodeJS.Process?這個類型。最后在使用到?foo?的時候,傳入了?node?中的全局變量?process。

由于引入的?node?中的類型都是全局變量的類型,它們是沒有辦法通過?import?來導(dǎo)入的,所以這種場景下也只能通過三斜線指令來引入了。

以上兩種使用場景下,都是由于需要書寫或需要依賴全局變量的聲明文件,所以必須使用三斜線指令。在其他的一些不是必要使用三斜線指令的情況下,就都需要使用?import?來導(dǎo)入。

拆分聲明文件§

當我們的全局變量的聲明文件太大時,可以通過拆分為多個文件,然后在一個入口文件中將它們一一引入,來提高代碼的可維護性。比如?jQuery?的聲明文件就是這樣的:

// node_modules/@types/jquery/index.d.ts/// <reference types="sizzle" />
/// <reference path="JQueryStatic.d.ts" />
/// <reference path="JQuery.d.ts" />
/// <reference path="misc.d.ts" />
/// <reference path="legacy.d.ts" />export = jQuery;

其中用到了?types?和?path?兩種不同的指令。它們的區(qū)別是:types?用于聲明對另一個庫的依賴,而?path?用于聲明對另一個文件的依賴。

上例中,sizzle?是與?jquery?平行的另一個庫,所以需要使用?types="sizzle"?來聲明對它的依賴。而其他的三斜線指令就是將?jquery?的聲明拆分到不同的文件中了,然后在這個入口文件中使用?path="foo"?將它們一一引入。

其他三斜線指令§

除了這兩種三斜線指令之外,還有其他的三斜線指令,比如?/// <reference no-default-lib="true"/>,?/// <amd-module />?等,但它們都是廢棄的語法,故這里就不介紹了,詳情可見官網(wǎng)。

自動生成聲明文件§

如果庫的源碼本身就是由 ts 寫的,那么在使用?tsc?腳本將 ts 編譯為 js 的時候,添加?declaration?選項,就可以同時也生成?.d.ts?聲明文件了。

我們可以在命令行中添加?--declaration(簡寫?-d),或者在?tsconfig.json?中添加?declaration?選項。這里以?tsconfig.json?為例:

{"compilerOptions": {"module": "commonjs","outDir": "lib","declaration": true,}
}

上例中我們添加了?outDir?選項,將 ts 文件的編譯結(jié)果輸出到?lib?目錄下,然后添加了?declaration?選項,設(shè)置為?true,表示將會由 ts 文件自動生成?.d.ts?聲明文件,也會輸出到?lib?目錄下。

運行?tsc?之后,目錄結(jié)構(gòu)如下30:

/path/to/project
├── lib
|  ├── bar
|  |  ├── index.d.ts
|  |  └── index.js
|  ├── index.d.ts
|  └── index.js
├── src
|  ├── bar
|  |  └── index.ts
|  └── index.ts
├── package.json
└── tsconfig.json

在這個例子中,src?目錄下有兩個 ts 文件,分別是?src/index.ts?和?src/bar/index.ts,它們被編譯到?lib?目錄下的同時,也會生成對應(yīng)的兩個聲明文件?lib/index.d.ts?和?lib/bar/index.d.ts。它們的內(nèi)容分別是:

// src/index.tsexport * from './bar';export default function foo() {return 'foo';
}
// src/bar/index.tsexport function bar() {return 'bar';
}
// lib/index.d.tsexport * from './bar';
export default function foo(): string;
// lib/bar/index.d.tsexport declare function bar(): string;

可見,自動生成的聲明文件基本保持了源碼的結(jié)構(gòu),而將具體實現(xiàn)去掉了,生成了對應(yīng)的類型聲明。

使用?tsc?自動生成聲明文件時,每個 ts 文件都會對應(yīng)一個?.d.ts?聲明文件。這樣的好處是,使用方不僅可以在使用?import foo from 'foo'?導(dǎo)入默認的模塊時獲得類型提示,還可以在使用?import bar from 'foo/lib/bar'?導(dǎo)入一個子模塊時,也獲得對應(yīng)的類型提示。

除了?declaration?選項之外,還有幾個選項也與自動生成聲明文件有關(guān),這里只簡單列舉出來,不做詳細演示了:

  • declarationDir?設(shè)置生成?.d.ts?文件的目錄
  • declarationMap?對每個?.d.ts?文件,都生成對應(yīng)的?.d.ts.map(sourcemap)文件
  • emitDeclarationOnly?僅生成?.d.ts?文件,不生成?.js?文件

發(fā)布聲明文件§

當我們?yōu)橐粋€庫寫好了聲明文件之后,下一步就是將它發(fā)布出去了。

此時有兩種方案:

  1. 將聲明文件和源碼放在一起
  2. 將聲明文件發(fā)布到?@types?下

這兩種方案中優(yōu)先選擇第一種方案。保持聲明文件與源碼在一起,使用時就不需要額外增加單獨的聲明文件庫的依賴了,而且也能保證聲明文件的版本與源碼的版本保持一致。

僅當我們在給別人的倉庫添加類型聲明文件,但原作者不愿意合并 pull request 時,才需要使用第二種方案,將聲明文件發(fā)布到?@types?下。

將聲明文件和源碼放在一起§

如果聲明文件是通過?tsc?自動生成的,那么無需做任何其他配置,只需要把編譯好的文件也發(fā)布到 npm 上,使用方就可以獲取到類型提示了。

如果是手動寫的聲明文件,那么需要滿足以下條件之一,才能被正確的識別:

  • 給?package.json?中的?types?或?typings?字段指定一個類型聲明文件地址
  • 在項目根目錄下,編寫一個?index.d.ts?文件
  • 針對入口文件(package.json?中的?main?字段指定的入口文件),編寫一個同名不同后綴的?.d.ts?文件

第一種方式是給?package.json?中的?types?或?typings?字段指定一個類型聲明文件地址。比如:

{"name": "foo","version": "1.0.0","main": "lib/index.js","types": "foo.d.ts",
}

指定了?types?為?foo.d.ts?之后,導(dǎo)入此庫的時候,就會去找?foo.d.ts?作為此庫的類型聲明文件了。

typings?與?types?一樣,只是另一種寫法。

如果沒有指定?types?或?typings,那么就會在根目錄下尋找?index.d.ts?文件,將它視為此庫的類型聲明文件。

如果沒有找到?index.d.ts?文件,那么就會尋找入口文件(package.json?中的?main?字段指定的入口文件)是否存在對應(yīng)同名不同后綴的?.d.ts?文件。

比如?package.json?是這樣時:

{"name": "foo","version": "1.0.0","main": "lib/index.js"
}

就會先識別?package.json?中是否存在?types?或?typings?字段。發(fā)現(xiàn)不存在,那么就會尋找是否存在?index.d.ts?文件。如果還是不存在,那么就會尋找是否存在?lib/index.d.ts?文件。假如說連?lib/index.d.ts?都不存在的話,就會被認為是一個沒有提供類型聲明文件的庫了。

有的庫為了支持導(dǎo)入子模塊,比如?import bar from 'foo/lib/bar',就需要額外再編寫一個類型聲明文件?lib/bar.d.ts?或者?lib/bar/index.d.ts,這與自動生成聲明文件類似,一個庫中同時包含了多個類型聲明文件。

將聲明文件發(fā)布到?@types?下§

如果我們是在給別人的倉庫添加類型聲明文件,但原作者不愿意合并 pull request,那么就需要將聲明文件發(fā)布到?@types?下。

與普通的 npm 模塊不同,@types?是統(tǒng)一由?DefinitelyTyped?管理的。要將聲明文件發(fā)布到?@types?下,就需要給?DefinitelyTyped?創(chuàng)建一個 pull-request,其中包含了類型聲明文件,測試代碼,以及?tsconfig.json?等。

pull-request 需要符合它們的規(guī)范,并且通過測試,才能被合并,稍后就會被自動發(fā)布到?@types?下。

在?DefinitelyTyped?中創(chuàng)建一個新的類型聲明,需要用到一些工具,DefinitelyTyped?的文檔中已經(jīng)有了詳細的介紹,這里就不贅述了,以官方文檔為準。

參考文檔

簡介 · TypeScript 入門教程

http://www.risenshineclean.com/news/795.html

相關(guān)文章:

  • 蔚縣做網(wǎng)站/云資源軟文發(fā)布平臺
  • 小企業(yè)網(wǎng)站建設(shè)的大品牌/優(yōu)化整站
  • 網(wǎng)站開發(fā)管理/網(wǎng)站優(yōu)化員seo招聘
  • 慶陽網(wǎng)站設(shè)計/創(chuàng)建自己的網(wǎng)頁
  • 做系統(tǒng)下載網(wǎng)站建設(shè)/最新經(jīng)濟新聞
  • 工會網(wǎng)站平臺建設(shè)/推廣普通話的宣傳標語
  • 做網(wǎng)站和做網(wǎng)頁一樣嗎/大數(shù)據(jù)查詢
  • 3d做號網(wǎng)站/刷推廣軟件
  • 網(wǎng)上購物系統(tǒng)源代碼/關(guān)鍵詞優(yōu)化建議
  • 北京 做網(wǎng)站/新站整站優(yōu)化
  • c語言在線編程網(wǎng)站/全自動推廣軟件
  • 怎么網(wǎng)站代備案/微信推廣方式有哪些
  • 網(wǎng)站規(guī)劃的一般步驟/搭建一個網(wǎng)站平臺需要多少錢
  • 石家莊網(wǎng)站搭建/我的百度賬號登錄
  • 做網(wǎng)站怎么加彈幕/營銷咨詢公司
  • 網(wǎng)站建設(shè)中的功能/百度地址
  • 素材網(wǎng)站下載/口碑營銷的主要手段有哪些
  • 校友網(wǎng)站 建設(shè)/強力搜索引擎
  • 怎么做百度網(wǎng)站/怎么建立個人網(wǎng)站
  • wordpress 作者簡介/東莞網(wǎng)站優(yōu)化關(guān)鍵詞排名
  • 域名 網(wǎng)站/站長之家網(wǎng)站介紹
  • 蘿崗區(qū)營銷型網(wǎng)站建設(shè)/少兒編程培訓(xùn)機構(gòu)排名前十
  • WordPress頂部廣告插件/seo搜索優(yōu)化專員
  • 惠州關(guān)鍵詞排名提升/河北seo推廣
  • 京東網(wǎng)站制作優(yōu)點/網(wǎng)站數(shù)據(jù)分析案例
  • 微信公眾號人工客服電話轉(zhuǎn)人工/南陽網(wǎng)站優(yōu)化公司
  • 怎么做論壇的網(wǎng)站/附近電腦培訓(xùn)速成班一個月
  • 做的網(wǎng)站圖片顯示一半/今日熱點事件
  • 做一款什么網(wǎng)站賺錢/2023免費推廣入口
  • 豬八戒網(wǎng)怎么做網(wǎng)站/電商運營培訓(xùn)班