国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

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

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

什么是聲明語句§

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

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

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

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

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

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

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

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

jQuery('#foo');

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

什么是聲明文件§

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

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

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

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

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

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

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

第三方聲明文件§

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

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

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

npm install @types/jquery --save-dev

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

書寫聲明文件§

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

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

庫的使用場(chǎng)景主要有以下幾種:

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

全局變量§

全局變量是最簡(jiǎn)單的一種場(chǎng)景,之前舉的例子就是通過?<script>?標(biāo)簽引入 jQuery,注入全局變量?$?和?jQuery。

使用全局變量的聲明文件時(shí),如果是以?npm install @types/xxx --save-dev?安裝的,則不需要任何配置。如果是將聲明文件直接存放于當(dāng)前項(xiàng)目中,則建議和其他源碼一起放到?src?目錄下(或者對(duì)應(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?聲明(含有子屬性的)全局對(duì)象
  • interface?和?type?聲明全局類型

declare var§

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

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

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

// src/jQuery.d.tsdeclare const jQuery: (selector: string) => any;jQuery('#foo');
// 使用 declare const 定義的 jQuery 類型,禁止修改這個(gè)全局變量
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

需要注意的是,聲明語句中只能定義類型,切勿在聲明語句中定義具體的實(shí)現(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í)就是一個(gè)函數(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§

當(dāng)全局變量是一個(gè)類的時(shí)候,我們用?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?語句也只能用來定義類型,不能用來定義具體的實(shí)現(xiàn),比如定義?sayHi?方法的具體實(shí)現(xiàn)則會(huì)報(bào)錯(cuò):

// 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?僅僅會(huì)用于編譯時(shí)的檢查,聲明文件里的內(nèi)容在編譯結(jié)果中會(huì)被刪除。它編譯結(jié)果是:

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

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

declare namespace§

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

由于歷史遺留原因,在早期還沒有 ES6 的時(shí)候,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?還是比較常用的,它用來表示全局變量是一個(gè)對(duì)象,包含很多子屬性。

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

// 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);

嵌套的命名空間§

如果對(duì)象擁有深層的層級(jí),則需要用嵌套的?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?這一個(gè)屬性(沒有?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?來聲明一個(gè)全局的接口或類型12:

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

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

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

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

防止命名沖突§

暴露在最外層的?interface?或?type?會(huì)作為全局類型作用于整個(gè)項(xiàng)目中,我們應(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;
}

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

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

聲明合并§

假如 jQuery 既是一個(gè)函數(shù),可以直接被調(diào)用?jQuery('#foo'),又是一個(gè)對(duì)象,擁有子屬性?jQuery.ajax()(事實(shí)確實(shí)如此),那么我們可以組合多個(gè)聲明語句,它們會(huì)不沖突的合并起來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)入一個(gè) npm 包,這是符合 ES6 模塊規(guī)范的。

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

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

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

  1. 創(chuàng)建一個(gè)?node_modules/@types/foo/index.d.ts?文件,存放?foo?模塊的聲明文件。這種方式不需要額外的配置,但是?node_modules?目錄不穩(wěn)定,代碼也沒有被保存到倉庫中,無法回溯版本,有不小心被刪除的風(fēng)險(xiǎn),故不太建議用這種方案,一般只用作臨時(shí)測(cè)試。
  2. 創(chuàng)建一個(gè)?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?的時(shí)候,也會(huì)去?types?目錄下尋找對(duì)應(yīng)的模塊的聲明文件了。

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

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

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

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

export§

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

export?的語法與普通的 ts 中的語法類似,區(qū)別僅在于聲明文件中禁止定義具體的實(shí)現(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;
}

對(duì)應(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?先聲明多個(gè)變量,最后再用?export?一次性導(dǎo)出。上例的聲明文件可以等價(jià)的改寫為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)出一個(gè)擁有子屬性的對(duì)象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)出一個(gè)默認(rèn)值,使用方可以用?import foo from 'foo'?而不是?import { foo } from 'foo'?來導(dǎo)入這個(gè)默認(rèn)值。

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

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

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

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

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

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

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

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

export =§

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

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

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

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

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

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

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

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

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

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

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

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

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

UMD 庫§

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

export as namespace§

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

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

當(dāng)然它也可以與?export default?一起使用:

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

直接擴(kuò)展全局變量§

有的第三方庫擴(kuò)展了一個(gè)全局變量,可是此全局變量的類型卻沒有相應(yīng)的更新過來,就會(huì)導(dǎo)致 ts 編譯錯(cuò)誤,此時(shí)就需要擴(kuò)展全局變量的類型。比如擴(kuò)展?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 庫中擴(kuò)展全局變量§

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

declare global§

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

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

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

模塊插件§

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

declare module§

如果是需要擴(kuò)展原有模塊的話,需要在類型聲明文件中先引用原有模塊,再使用?declare module?擴(kuò)展原有模塊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?也可用于在一個(gè)文件中一次性聲明多個(gè)模塊的類型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();

聲明文件中的依賴§

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

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

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

三斜線指令§

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

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

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

  • 當(dāng)我們?cè)?strong>書寫一個(gè)全局變量的聲明文件時(shí)
  • 當(dāng)我們需要依賴一個(gè)全局變量的聲明文件時(shí)

書寫一個(gè)全局變量的聲明文件§

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

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

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

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

依賴一個(gè)全局變量的聲明文件§

在另一個(gè)場(chǎng)景下,當(dāng)我們需要依賴一個(gè)全局變量的聲明文件時(shí),由于全局變量不支持通過?import?導(dǎo)入,當(dāng)然也就必須使用三斜線指令來引入了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?這個(gè)類型。最后在使用到?foo?的時(shí)候,傳入了?node?中的全局變量?process。

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

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

拆分聲明文件§

當(dāng)我們的全局變量的聲明文件太大時(shí),可以通過拆分為多個(gè)文件,然后在一個(gè)入口文件中將它們一一引入,來提高代碼的可維護(hù)性。比如?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?用于聲明對(duì)另一個(gè)庫的依賴,而?path?用于聲明對(duì)另一個(gè)文件的依賴。

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

其他三斜線指令§

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

自動(dòng)生成聲明文件§

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

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

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

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

運(yùn)行?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

在這個(gè)例子中,src?目錄下有兩個(gè) ts 文件,分別是?src/index.ts?和?src/bar/index.ts,它們被編譯到?lib?目錄下的同時(shí),也會(huì)生成對(duì)應(yīng)的兩個(gè)聲明文件?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;

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

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

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

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

發(fā)布聲明文件§

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

此時(shí)有兩種方案:

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

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

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

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

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

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

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

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

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

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

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

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

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

比如?package.json?是這樣時(shí):

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

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

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

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

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

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

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

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

參考文檔

簡(jiǎn)介 · TypeScript 入門教程

http://m.aloenet.com.cn/news/795.html

相關(guān)文章:

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