廣義來說,宣告檔案的結構方式取決於如何使用函式庫。有許多方法可以在 JavaScript 中提供函式庫供使用,而且您需要撰寫宣告檔案來與其相符。本指南說明如何識別常見的函式庫模式,以及如何撰寫與該模式對應的宣告檔案。
每種類型的主要函式庫結構模式在範本區段中都有對應的檔案。您可以從這些範本開始,協助您更快上手。
識別函式庫類型
首先,我們將檢閱 TypeScript 宣告檔案可以代表的函式庫類型。我們將簡要說明每種函式庫的使用方式、撰寫方式,並列出一些來自真實世界的範例函式庫。
識別函式庫的結構是撰寫其宣告檔案的第一步。我們將提供如何根據其用法和程式碼識別結構的提示。根據函式庫的說明文件和組織方式,其中一種可能比另一種容易。我們建議使用對你來說比較舒適的方式。
你應該尋找什麼?
在你嘗試輸入函式庫時,問自己的問題。
-
你如何取得函式庫?
例如,你只能透過 npm 或只能從 CDN 取得嗎?
-
你如何匯入它?
它會新增一個全域物件嗎?它使用
require
或import
/export
陳述式嗎?
不同類型函式庫的較小範例
模組化函式庫
幾乎每個現代 Node.js 函式庫都屬於模組系列。這種類型的函式庫僅在具有模組載入器的 JS 環境中運作。例如,express
僅在 Node.js 中運作,且必須使用 CommonJS require
函式載入。
ECMAScript 2015(也稱為 ES2015、ECMAScript 6 和 ES6)、CommonJS 和 RequireJS 對於匯入模組有類似的概念。例如,在 JavaScript CommonJS(Node.js)中,您可以撰寫
js
var fs = require("fs");
在 TypeScript 或 ES6 中,import
關鍵字具有相同目的
ts
import * as fs from "fs";
您通常會看到模組化函式庫在其文件中有以下其中一行程式碼
js
var someLib = require("someLib");
或
js
define(..., ['someLib'], function(someLib) {});
與全域模組一樣,您可能會在 UMD 模組的文件中看到這些範例,因此請務必查看程式碼或文件。
從程式碼辨識模組化函式庫
模組化函式庫通常至少會具備下列其中幾項
- 無條件呼叫
require
或define
- 宣告例如
import * as a from 'b';
或export c;
- 指定給
exports
或module.exports
它們很少會
- 指定給
window
或global
的屬性
模組範本
模組有四個可用的範本,module.d.ts
、module-class.d.ts
、module-function.d.ts
和 module-plugin.d.ts
。
您應該先閱讀 module.d.ts
,以了解它們運作方式的概觀。
然後,如果您的模組可以像函式一樣被呼叫,請使用範本 module-function.d.ts
js
const x = require("foo");// Note: calling 'x' as a functionconst y = x(42);
如果您的模組可以使用 new
進行建構,請使用範本 module-class.d.ts
js
const x = require("bar");// Note: using 'new' operator on the imported variableconst y = new x("hello");
如果您有一個模組,在匯入時會對其他模組進行變更,請使用範本 module-plugin.d.ts
js
const jest = require("jest");require("jest-matchers-files");
全域函式庫
全域函式庫是可以從全域範圍存取的函式庫(亦即不用任何形式的 import
)。許多函式庫只會公開一個或多個全域變數供使用。例如,如果你使用 jQuery,只要參照 $
變數即可使用
ts
$(() => {console.log("hello!");});
你通常會在全域函式庫的文件中看到如何使用 HTML 腳本標籤中的函式庫的說明
html
<script src="http://a.great.cdn.for/someLib.js"></script>
現今,大多數熱門的全域存取函式庫實際上都是以 UMD 函式庫寫成的(請見下方)。UMD 函式庫的文件很難與全域函式庫的文件區分。在撰寫全域宣告檔之前,請確定函式庫實際上不是 UMD。
從程式碼辨識全域函式庫
全域程式庫程式碼通常非常簡單。一個全域「Hello, world」程式庫可能如下所示
js
function createGreeting(s) {return "Hello, " + s;}
或如下所示
js
// Webwindow.createGreeting = function (s) {return "Hello, " + s;};// Nodeglobal.createGreeting = function (s) {return "Hello, " + s;};// Potentially any runtimeglobalThis.createGreeting = function (s) {return "Hello, " + s;};
在檢視全域程式庫的程式碼時,您通常會看到
- 頂層
var
陳述式或function
宣告 - 一個或多個指派給
window.someName
- 假設 DOM 原生函數(例如
document
或window
)存在
您不會看到
- 檢查或使用模組載入器(例如
require
或define
) - CommonJS/Node.js 風格的匯入,例如
var fs = require("fs");
- 呼叫
define(...)
- 說明如何
require
或匯入程式庫的文件
全域程式庫範例
由於通常很容易將全域程式庫轉換為 UMD 程式庫,因此很少有熱門程式庫仍以全域風格撰寫。但是,體積小且需要 DOM(或沒有相依項目的)程式庫可能仍然是全域的。
全域程式庫範本
範本檔案 global.d.ts
定義範例函式庫 myLib
。請務必閱讀 「防止名稱衝突」註腳。
UMD
UMD 模組可以同時作為模組(透過匯入)或全域變數(在沒有模組載入器的環境中執行時)使用。許多熱門函式庫(例如 Moment.js)都是以這種方式撰寫的。例如,在 Node.js 中或使用 RequireJS 時,您會撰寫
ts
import moment = require("moment");console.log(moment.format());
而在純瀏覽器環境中,您會撰寫
js
console.log(moment.format());
辨識 UMD 函式庫
UMD 模組會檢查模組載入器環境是否存在。這是一個容易發現的模式,看起來像這樣
js
(function (root, factory) {if (typeof define === "function" && define.amd) {define(["libName"], factory);} else if (typeof module === "object" && module.exports) {module.exports = factory(require("libName"));} else {root.returnExports = factory(root.libName);}}(this, function (b) {
如果您在函式庫的程式碼中看到對 typeof define
、typeof window
或 typeof module
的測試,特別是在檔案開頭,那幾乎肯定是一個 UMD 函式庫。
UMD 函式庫的文件通常也會展示一個「在 Node.js 中使用」範例,顯示 require
,以及一個「在瀏覽器中使用」範例,顯示使用 <script>
標籤載入腳本。
UMD 函式庫範例
大多數熱門函式庫現在都以 UMD 套件的形式提供。範例包括 jQuery、Moment.js、lodash,以及更多其他函式庫。
範本
使用 module-plugin.d.ts
範本。
使用相依性
您的函式庫可能有幾種相依性。本節說明如何將它們匯入宣告檔。
對全域函式庫的相依性
如果您的函式庫依賴於全域函式庫,請使用 /// <reference types="..." />
指令
ts
/// <reference types="someLib" />function getThing(): someLib.thing;
對模組的相依性
如果您的函式庫依賴於模組,請使用 import
陳述式
ts
import * as moment from "moment";function getThing(): moment;
對 UMD 函式庫的相依性
來自全球函式庫
如果你的全球函式庫依賴 UMD 模組,請使用 /// <reference types
指令
ts
/// <reference types="moment" />function getThing(): moment;
來自模組或 UMD 函式庫
如果你的模組或 UMD 函式庫依賴 UMD 函式庫,請使用 import
陳述式
ts
import * as someLib from "someLib";
不要使用 /// <reference
指令來宣告對 UMD 函式庫的依賴關係!
註腳
防止名稱衝突
請注意,撰寫全域宣告檔時,可以在全域範圍內定義許多類型。我們強烈建議不要這樣做,因為當專案中有許多宣告檔時,可能會導致無法解決的名稱衝突。
要遵循的簡單規則是,僅宣告由程式庫定義的任何全域變數命名空間的類型。例如,如果程式庫定義全域值「cats」,您應該撰寫
ts
declare namespace cats {interface KittySettings {}}
但不
ts
// at top-levelinterface CatsKittySettings {}
此指南還可確保程式庫可以在不中斷宣告檔使用者的情況下轉換為 UMD。
ES6 對模組呼叫簽章的影響
許多熱門程式庫(例如 Express)在匯入時會將自己顯示為可呼叫函式。例如,典型的 Express 使用方式如下所示
ts
import exp = require("express");var app = exp();
在符合 ES6 的模組載入器中,頂層物件(在此匯入為 exp
)只能有屬性;頂層模組物件永遠不能呼叫。
最常見的解決方案是在此為可呼叫/可建構物件定義 default
匯出;模組載入器通常會自動偵測此情況,並用 default
匯出取代頂層物件。如果您在 tsconfig.json 中有 "esModuleInterop": true
,TypeScript 可以為您處理這項工作。