最近有個需求,須要寫聲明文件。雖然一直有在用typescript
,可是對聲明文件相關信息沒有怎麼使用過,因而記錄一下。javascript
在使用第三方庫的時候,想使用typescript
類型檢查、自動補全等等功能,須要一個描述javascript
庫和模塊信息的聲明文件。一般來講,都是將聲明語句放到一個單獨文件中*.d.ts
。html
對於第三方庫,目前也是DefinitelyTyped推薦的兩種方式:java
typescript
,那麼推薦在包裏捆綁自動生成的聲明文件。補充:在tsconfig.json
裏,能夠設置如下屬性去自動生成聲明文件:
declaration
:設置能夠自動生成*.d.ts
聲明文件declarationDir
:設置生成的*.d.ts
聲明文件的目錄declarationMap
:設置生成*.d.ts.map
文件(sourceMap)emitDeclarationOnly
:不生成js
文件,僅僅生成*.d.ts
和*.d.ts.map
typescript
,那麼能夠選擇發起一個PR
給DefinitelyTyped。若是合併到了master
上,會自動發佈到npm
上。即:@types/xxx
。*.d.ts
和*.ts
的區別在於:node
*.d.ts
對於typescript
而言,是類型聲明文件,且在*.d.ts
文件中的頂級聲明必須以declare
或export
修飾符開頭。同時在項目編譯事後,*.d.ts
文件是不會生成任何代碼的。補充:默認使用tsc —init
會開啓skipLibCheck
跳過聲明文件檢查,能夠關閉它。*.ts
則沒有那麼多限制,任何在*.d.ts
中的內容,都可以在*.ts
中使用。同時根據文檔,在typescript 2.0
之後,默認全部可見的@types
包,會在編譯過程當中包含進來,例如:./node_modules/@types/
、../node_modules/@types/
和../../node_modules/@types/
等等。react
可是,若是指定了typeRoots
或者types
,那麼只有typeRoots
目錄下的包纔會被引入,或者被types
指定的包。git
例如:設置"types": []
會禁用自動引入@types
包的功能。es6
如下語法並不只僅只能在聲明文件中,只是說,至關於普通文件書寫,更頻繁出如今聲明文件中。github
在聲明文件中,最常看見的語法之一。用來全局聲明變量、常量、類、全局對象等等,前提是該文件不是模塊聲明文件(後面會講)。typescript
declare const Jye1: string;
declare let Jye2: string;
declare class Jye3 {}
declare namespace Jye4 {}
// ...
複製代碼
同時在聲明函數的時候,也是支持函數重載的。npm
declare function name(params: string): void;
declare function name(params: number): number;
複製代碼
在使用declare
聲明類型的時候,並不能去定義具體的實現過程。
比較特別的,像是經過declare global
,能夠拓展全局變量的類型和方法。
// ./types/test.d.ts
declare global {
interface String {
helloword(): string;
}
}
export {};
複製代碼
// ./src/test.ts
const test = "jye";
test.helloword();
複製代碼
若是不加export {}
,會報「全局範圍的擴大僅可直接嵌套在外部模塊中或環境模塊聲明中」錯誤。增長export{}
其實也就是爲了讓這個聲明文件變成模塊聲明文件,而不是一個全局聲明文件。
前言:在typescript 1.5
裏,內部模塊被稱作「命名空間」,外部模塊稱爲「模塊」。同時module X {
至關於如今推薦的寫法namespace X {
。文檔
namespace
一開始的提出,主要是爲了模塊化(防止命名衝突等等)。可是ES6
普及以後,namespace
已經再也不推薦使用了,更推薦使用ES6
模塊化。可是,在聲明文件中namespace
比較常見的。
命名空間表示一個全局變量是一個對象,能夠定義不少屬性類型。同時命名空間裏可能會用到一些接口類型(interface
、type
),這時候通常有兩種寫法:
namespace
外層,會做爲全局類型被引入,從而可能污染全局類型空間。namespace
裏層,在想使用該類型的時候,能夠經過namespace.interface
進行使用。(推薦)// ./types/test.d.ts
declare namespace Jye {
interface Info {
name: string;
age: number;
}
function getAge(): number;
}
複製代碼
// ./src/test.ts
let settings: Jye.Info = {
name: "jye",
age: 8,
};
Jye.getAge();
複製代碼
同時,命名空間支持嵌套使用,即:namespace
嵌套namepsace
。或者簡化的寫法,能夠寫成namepsace.namespace
進行聲明。
同時命名空間也支持聲明合併。
// ./types/test.d.ts
declare namespace Jye.Eee {
interface Api {
getInfo(): Info;
}
}
複製代碼
三斜線指令,也是最初用來表示模塊之間依賴關係。目前也是不多會去使用,不過聲明文件中,仍是有不少會去使用。
在三斜線指令的語法中,目前可能會去比較經常使用的兩種語法:
/// <reference path="./lib/index.d.ts" />
:表示對一個文件的依賴。/// <reference types="jye" />
:表示對一個庫的依賴。說白了,三斜線的path
& types
,和es6
的import
語義類似,同時三斜線指令必須放在文件的最頂端。例如,當咱們的聲明文件過於龐大,通常都會採用三斜線指令,將咱們的聲明文件拆分紅若干個,而後由一個入口文件引入。
在配置tsconfig.json
設置declaration
爲ture
去自動生成聲明文件或者是手動去寫聲明文件,比較常見的語法,像是:
export
:導出變量export default
: 默認導出export namespace
:導出對象export =
:commonJS導出npm
包的聲明文件相對於以前的全局聲明文件而言,能夠理解爲是局部聲明文件。只有當經過import
引入npm
包後,才能使用對應的聲明類型。而前三個語法,其實和es6
相似,用法語義一目瞭然。
比較特殊的是,export =
對應的像是import xxx = require
。其實使用都是相似的,只是爲了兼容AMD
和commonJS
纔有的語法。文檔
其實也就是說,對於一個npm
包的聲明文件,只有經過export
導出的類型,才能被使用。
其實寫到這裏,前面有兩點沒有說清楚,什麼是全局聲明,什麼是局部聲明。
個人理解是,若是這個聲明文件被typescript
引入了,那麼這個文件不包含import
export
,那麼這個文件中包含的declare & interface & type
就會變成全局聲明。反之,如果這個文件包含了import export
,那麼這個文件包含的declare & interface & type
則會是局部聲明,不會影響到全局聲明。
以@types/react
爲例:配置tsconfig.json
關閉自動引入@types
文件,且在@types/react
中增長declare
:
// @types/react/index.d.ts
export = React;
export as namespace React;
declare namespace Jye {
interface Info {
name: string;
age: number;
}
function getAge(): number;
}
複製代碼
同時在a
文件import React from 'react
,在b
文件使用相關類型
// src/b.ts
React.Children; // ok
let settings: Jye.Info = { // 找不到命名空間「Jye」。ts(2503)
name: 'jye',
age: 8,
};
Jye.getAge(); // 找不到命名空間「Jye」。ts(2503)
複製代碼
能夠看到,在項目中引入了react
後,那麼該文件導出的類型則被引入到全局中。可是除卻export
出來的類型,其餘declare
的類型,則沒法被使用。
同理,能夠在項目中,定義*d.ts
,經過設置export {}
將其從一個全局聲明文件變成一個模塊聲明文件。那麼對應declare
內容則會沒法使用,只能經過引入文件後,使用其export
出來的類型。
那麼總結:若是沒有export import
,那麼這個文件被引入後,則會是一個全局聲明,(也就是說這個文件是全局聲明文件)。不然,這個文件被引入後,僅僅其export
的內容,被引入到全局裏,其餘內容則做爲局部聲明(這個文件是模塊聲明文件)。
在項目中,設置package.json
的types
或者typings
指向聲明文件。在設置types
或者typings
後,會去找指向的聲明文件。若是沒有定義,則會去找根目錄下的index.d.ts
,再沒有則去找入口文件,是否存在對應文件名的聲明文件。
具體typescript
如何解析查找模塊類型,能夠看這篇文章傳送門
能夠經過如下方法去讓typescript
引入類型:
tsconfig.json
配置types
指定咱們的包名。import
手動導入咱們的包。