當(dāng)使用第三方庫(kù)時(shí),需要引用它的聲明文件,才能獲知其數(shù)據(jù)類(lèi)型,以獲得對(duì)應(yīng)的代碼補(bǔ)全、接口提示等功能。
聲明文件用于定義類(lèi)型而非具體的值,不會(huì)保留在編譯結(jié)果的 js 中。
TS編譯檢查時(shí),變量的聲明查找順序
- 當(dāng)前編譯上下文找該變量的定義
- 變量所在模塊的
index.d.ts聲明文件中查找(或其package.json中types字段指向文件)
通常由npm包的維護(hù)者提供 -
node_modules/@types/npm包名查找聲明
通常是作者沒(méi)有提供聲明文件,由社區(qū)其他開(kāi)發(fā)者提供,以@types/開(kāi)頭的聲明文件,如:npm install @types/jquery - 通過(guò)配置文件
tsconfig.json中的paths和baseUrl字段指向的其他目錄
通常是自己本地寫(xiě)的聲明文件
生成 .d.ts 文件
在tsc指令中添加 --declaration(簡(jiǎn)寫(xiě) -d),或者在 tsconfig.json 中添加 declaration 選項(xiàng),就可以同時(shí)也生成 .d.ts 聲明文件了。
{
"compilerOptions": {
"module": "commonjs",
"outDir": "lib",
"declaration": true,
}
}
全局變量聲明
在script模式中,通過(guò)declare進(jìn)行全局變量聲明,通過(guò)script標(biāo)簽引入,全局生效。
注意全局變量文件中不能有import、export關(guān)鍵字,否則會(huì)被視為npm/UMD包文件。
type 和 interface
兩者本來(lái)就只是類(lèi)型的定義,因此不需搭配declare關(guān)鍵字,直接在聲明文件中使用
為了防止命名沖突,通常放在namespace下
聲明全局變量
declare var jQuery: (selector: string) => any;
declare let jQuery: (selector: string) => any;
declare const jQuery: (selector: string) => any;
聲明函數(shù)
支持函數(shù)重載
declare function jQuery(selector: string): any;
declare function jQuery(domReadyCallback: () => any): any;
聲明類(lèi)
注意 declare 里可以聲明類(lèi)的構(gòu)造函數(shù),interface 里不可以
declare class Animal {
name: string;
constructor(name: string);
sayHi(): string;
}
聲明枚舉
declare enum Directions {
Up,
Down,
Left,
Right
}
聲明命名空間
命名空間(namespace)是TS自己的模塊化方案,隨著ES6的module廣泛運(yùn)用,已不推薦使用。不同于ES6的module,一個(gè)文件里可以有多個(gè)namespace。
目前通常在聲明文件中使用,表示一個(gè)對(duì)象中具有很多子屬性。
通過(guò)命名空間.屬性來(lái)調(diào)用內(nèi)部屬性。
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
const version: number;
class Event {
blur(eventType: EventType): void
}
enum EventType {
CustomClick
}
}
命名空間也可以嵌套
// src/jQuery.d.ts
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
namespace fn {
function extend(object: any): void;
}
}
//或
declare namespace jQuery.fn {
function extend(object: any): void;
}
// src/index.ts
jQuery.ajax('/api/get_something');
jQuery.fn.extend({
check: function() {
return this.each(function() {
this.checked = true;
});
}
});
聲明合并
同名變量可以重復(fù)聲明,表示有不同的形狀
declare function jQuery(selector: string): any;
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
}
通過(guò)聲明合并,還可以為系統(tǒng)變量添加新屬性,稱(chēng)為擴(kuò)展全局變量:
interface String {
prependHello(): string;
}
模塊聲明
npm包(ES6)規(guī)范的聲明
.d.ts文件中,如存在export,則為一個(gè)模塊聲明文件,npm包中即使用該方式。
在模塊聲明中,只有export導(dǎo)出 + import導(dǎo)入的聲明,才會(huì)生效。
可以對(duì)每個(gè)變量分別export,也可以用declare分別聲明,并統(tǒng)一export(注意,同全局變量聲明文件,type 和 interface 不需要 declare)
// types/foo/index.d.ts
export const name: string;
export interface Options {
data: any;
}
// types/foo/index.d.ts
declare const name: string;
interface Options {
data: any;
}
export { name, Options };
// src/index.ts
import { name, Options } from 'foo';
console.log(name);
let options: Options = {
data: {
name: 'foo'
}
};
commonjs 規(guī)范的聲明
ts中,對(duì)采用commonjs規(guī)范的庫(kù),可以通過(guò)以下方式導(dǎo)入:
- commonjs語(yǔ)法:
const foo = require('foo');
const bar = require('foo').bar;
- ESM語(yǔ)法:
import * as foo from 'foo';
import { bar } from 'foo';
- TS新增語(yǔ)法
import + require和export =搭配使用:
// types/foo/index.d.ts
export = foo;
declare function foo(): string;
declare namespace foo {
const bar: number;
}
// 整體導(dǎo)入
import foo = require('foo');
import * as foo from 'foo';
import foo from 'foo';//當(dāng)啟用allowSyntheticDefaultImports時(shí)
// 單個(gè)導(dǎo)入
import bar = foo.bar;
UMD規(guī)范的聲明
既可以通過(guò) <script> 標(biāo)簽引入,又可以通過(guò) import 導(dǎo)入的庫(kù),需要通過(guò)export as namespace將聲明好的一個(gè)變量聲明為全局變量
// types/foo/index.d.ts
export as namespace foo;
export = foo;
declare function foo(): string;
declare namespace foo {
const bar: number;
}
直接擴(kuò)展全局變量
有的第三方庫(kù)擴(kuò)展了一個(gè)全局變量,可是此全局變量的類(lèi)型卻沒(méi)有相應(yīng)的更新過(guò)來(lái),就會(huì)導(dǎo)致 ts 編譯錯(cuò)誤,此時(shí)就需要擴(kuò)展全局變量的類(lèi)型。
注意聲明文件仍然需要導(dǎo)出一個(gè)空對(duì)象,用來(lái)告訴編譯器這是一個(gè)模塊的聲明文件
// types/foo/index.d.ts
declare global {
//通過(guò)聲明合并,給 String 添加屬性或方法。
interface String {
prependHello(): string;
}
}
export {};
// src/index.ts
'bar'.prependHello();
模塊插件
declare module可用于在一個(gè)文件中一次性聲明多個(gè)模塊的類(lèi)型,或擴(kuò)展某模塊類(lèi)型
// types/moment-plugin/index.d.ts
import * as moment from 'moment';
declare module 'moment' {
export function foo(): moment.CalendarKey;
}
// src/index.ts
import * as moment from 'moment';
import 'moment-plugin';
moment.foo();
三斜線指令
類(lèi)似于聲明文件中的 import,它可以用來(lái)導(dǎo)入另一個(gè)聲明文件。
三斜線指令必須放在文件的最頂端,三斜線指令的前面只允許出現(xiàn)單行或多行注釋。
當(dāng)且僅當(dāng)在以下幾個(gè)場(chǎng)景下,我們才需要使用三斜線指令替代 import:
- 當(dāng)我們?cè)跁?shū)寫(xiě)一個(gè)全局變量的聲明文件時(shí)(避免出現(xiàn)
import、export關(guān)鍵字,以免被識(shí)別為npm、UMD文件)
// types/jquery-plugin/index.d.ts
/// <reference types="jquery" />
declare function foo(options: JQuery.AjaxSettings): string;
// src/index.ts
foo({});
- 當(dāng)我們需要依賴(lài)一個(gè)全局變量的聲明文件時(shí)
由于全局變量不支持通過(guò) import 導(dǎo)入,當(dāng)然也就必須使用三斜線指令來(lái)引入了
// types/node-plugin/index.d.ts
/// <reference types="node" />
export function foo(p: NodeJS.Process): string;
// src/index.ts
import { foo } from 'node-plugin';
foo(global.process);