全域:外掛

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 definetypeof windowtypeof module 的測試,特別是在檔案開頭,那幾乎總是 UMD 函式庫。

UMD 函式庫的文件通常也會展示一個「在 Node.js 中使用」範例,顯示 require,以及一個「在瀏覽器中使用」範例,顯示使用 <script> 標籤載入指令碼。

UMD 函式庫範例

現在大多數熱門函式庫都以 UMD 套件的形式提供。範例包括 jQueryMoment.jslodash 等許多其他函式庫。

範本

模組有三個可用的範本,module.d.tsmodule-class.d.tsmodule-function.d.ts

如果您的模組可以像函式一樣被呼叫,請使用 module-function.d-ts

js
var x = require("foo");
// Note: calling 'x' as a function
var y = x(42);

務必閱讀註腳「ES6 對模組呼叫簽章的影響」

如果您的模組可以使用 new 建構,請使用 module-class.d.ts

js
var x = require("bar");
// Note: using 'new' operator on the imported variable
var y = new x("hello");

相同的註腳適用於這些模組。

如果您的模組不可呼叫或不可建構,請使用 module.d.ts 檔案。

模組外掛程式UMD 外掛程式

模組外掛程式會變更另一個模組(UMD 或模組)的形狀。例如,在 Moment.js 中,moment-range 會新增一個新的 range 方法到 moment 物件。

為了撰寫宣告檔,不論要變更的模組是純模組或 UMD 模組,您都會撰寫相同的程式碼。

範本

使用 module-plugin.d.ts 範本。

全球外掛

全球外掛是會改變某個全域變數形狀的全球程式碼。與修改全域變數的模組一樣,這些外掛程式會導致執行時期衝突。

例如,有些函式庫會新增新的函式到 Array.prototypeString.prototype

辨識全球外掛

從文件通常很容易辨識出全球外掛。

您會看到類似這樣的範例

js
var x = "hello, world";
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());

範本

使用 global-plugin.d.ts 範本。

全域修改模組

全域修改模組在匯入時會變更全域範圍內現有的值。例如,可能有一個函式庫在匯入時會新增新的成員到 String.prototype。這種模式有點危險,因為可能會發生執行時期衝突,但我們仍然可以為它撰寫宣告檔。

識別全域修改模組

全域修改模組通常很容易從其文件識別。一般來說,它們類似於全域外掛程式,但需要 require 呼叫才能啟動其效果。

您可能會看到類似這樣的文件

js
// 'require' call that doesn't use its return value
var unused = require("magic-string-time");
/* or */
require("magic-string-time");
var x = "hello, world";
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());

範本

使用 global-modifying-module.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-level
interface CatsKittySettings {}

此指南還可確保函式庫可以在不中斷宣告檔使用者的情況下轉換為 UMD。

ES6 對模組外掛程式的影響

有些外掛程式會新增或修改現有模組的頂層匯出。儘管這在 CommonJS 和其他載入器中是合法的,但 ES6 模組被視為不可變,因此這種模式將不可行。由於 TypeScript 與載入器無關,因此不會在編譯時強制執行此政策,但打算轉換到 ES6 模組載入器的開發人員應注意這一點。

ES6 對模組呼叫簽章的影響

許多熱門函式庫(例如 Express)在匯入時會將自己公開為可呼叫函式。例如,典型的 Express 使用方式如下所示

ts
import exp = require("express");
var app = exp();

在 ES6 模組載入器中,頂層物件(在此匯入為 exp)只能有屬性;頂層模組物件永遠不可呼叫。最常見的解決方案是在此為可呼叫/可建構物件定義 default 匯出;有些模組載入器 shim 會自動偵測此情況,並將頂層物件替換為 default 匯出。

函式庫檔案配置

宣告檔案的配置應反映函式庫的配置。

函式庫可以包含多個模組,例如

myLib +---- index.js +---- foo.js +---- bar +---- index.js +---- baz.js

這些模組可以匯入為

js
var a = require("myLib");
var b = require("myLib/foo");
var c = require("myLib/bar");
var d = require("myLib/bar/baz");

因此,您的宣告檔案應為

@types/myLib +---- index.d.ts +---- foo.d.ts +---- bar +---- index.d.ts +---- baz.d.ts
ts
// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
// Project: [~THE PROJECT NAME~]
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
/*~ This template shows how to write a global plugin. */
/*~ Write a declaration for the original type and add new members.
*~ For example, this adds a 'toBinaryString' method with overloads to
*~ the built-in number type.
*/
interface Number {
toBinaryString(opts?: MyLibrary.BinaryFormatOptions): string;
toBinaryString(
callback: MyLibrary.BinaryFormatCallback,
opts?: MyLibrary.BinaryFormatOptions
): string;
}
/*~ If you need to declare several types, place them inside a namespace
*~ to avoid adding too many things to the global namespace.
*/
declare namespace MyLibrary {
type BinaryFormatCallback = (n: number) => string;
interface BinaryFormatOptions {
prefix?: string;
padding: number;
}
}

TypeScript 文件是一個開放原始碼專案。透過 傳送 Pull Request ❤ 來協助我們改善這些頁面

此頁面的貢獻者
MHMohamed Hegazy (53)
OTOrta Therox (16)
MFMartin Fischer (1)
JHJonathan Harrison (1)
JJohnny (1)
2+

最後更新:2024 年 3 月 21 日