typescript 全局變量聲明文件和模塊聲明文件那些事兒

前言

最近有個需求,須要寫聲明文件。雖然一直有在用typescript,可是對聲明文件相關信息沒有怎麼使用過,因而記錄一下。javascript

概念簡述

聲明文件

在使用第三方庫的時候,想使用typescript類型檢查、自動補全等等功能,須要一個描述javascript 庫和模塊信息的聲明文件。一般來講,都是將聲明語句放到一個單獨文件中*.d.tshtml

對於第三方庫,目前也是DefinitelyTyped推薦的兩種方式:java

  • 若是是開發者,且使用的也是typescript,那麼推薦在包裏捆綁自動生成的聲明文件。補充:在tsconfig.json裏,能夠設置如下屬性去自動生成聲明文件:
    • declaration:設置能夠自動生成*.d.ts聲明文件
    • declarationDir:設置生成的*.d.ts聲明文件的目錄
    • declarationMap:設置生成*.d.ts.map文件(sourceMap)
    • emitDeclarationOnly:不生成js文件,僅僅生成*.d.ts*.d.ts.map
  • 若是不是開發者或者不是用typescript,那麼能夠選擇發起一個PRDefinitelyTyped。若是合併到了master上,會自動發佈到npm上。即:@types/xxx

聲明文件和普通文件

*.d.ts*.ts的區別在於:node

  • *.d.ts對於typescript而言,是類型聲明文件,且在*.d.ts文件中的頂級聲明必須以declareexport修飾符開頭。同時在項目編譯事後,*.d.ts文件是不會生成任何代碼的。補充:默認使用tsc —init會開啓skipLibCheck跳過聲明文件檢查,能夠關閉它。
  • *.ts則沒有那麼多限制,任何在*.d.ts中的內容,都可以在*.ts中使用。

自動引入@types

同時根據文檔,在typescript 2.0之後,默認全部可見的@types包,會在編譯過程當中包含進來,例如:./node_modules/@types/../node_modules/@types/../../node_modules/@types/等等。react

可是,若是指定了typeRoots或者types,那麼只有typeRoots目錄下的包纔會被引入,或者被types指定的包。git

例如:設置"types": []會禁用自動引入@types包的功能。es6

聲明文件實現

如下語法並不只僅只能在聲明文件中,只是說,至關於普通文件書寫,更頻繁出如今聲明文件中。github

declare

在聲明文件中,最常看見的語法之一。用來全局聲明變量、常量、類、全局對象等等,前提是該文件不是模塊聲明文件(後面會講)。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比較常見的。

命名空間表示一個全局變量是一個對象,能夠定義不少屬性類型。同時命名空間裏可能會用到一些接口類型(interfacetype),這時候通常有兩種寫法:

  • 寫在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,和es6import語義類似,同時三斜線指令必須放在文件的最頂端。例如,當咱們的聲明文件過於龐大,通常都會採用三斜線指令,將咱們的聲明文件拆分紅若干個,而後由一個入口文件引入。

npm包捆綁的聲明文件語法

在配置tsconfig.json設置declarationture去自動生成聲明文件或者是手動去寫聲明文件,比較常見的語法,像是:

  • export:導出變量
  • export default: 默認導出
  • export namespace:導出對象
  • export =:commonJS導出

npm包的聲明文件相對於以前的全局聲明文件而言,能夠理解爲是局部聲明文件。只有當經過import引入npm包後,才能使用對應的聲明類型。而前三個語法,其實和es6相似,用法語義一目瞭然。

比較特殊的是,export =對應的像是import xxx = require。其實使用都是相似的,只是爲了兼容AMDcommonJS纔有的語法。文檔

其實也就是說,對於一個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.jsontypes或者typings指向聲明文件。在設置types或者typings後,會去找指向的聲明文件。若是沒有定義,則會去找根目錄下的index.d.ts,再沒有則去找入口文件,是否存在對應文件名的聲明文件。

具體typescript如何解析查找模塊類型,能夠看這篇文章傳送門

能夠經過如下方法去讓typescript引入類型:

  • tsconfig.json配置types指定咱們的包名。
  • 在項目中,經過import手動導入咱們的包。
  • 在項目中,經過三斜線指令引用。

參考資料

www.typescriptlang.org/

jkchao.github.io/typescript-…

ts.xcatliu.com/

相關文章
相關標籤/搜索