全域性:外掛

UMD

UMD 模組是指既可以作為模組使用(透過 import),也可以作為全域性變數使用(在沒有模組載入器的環境中執行時)的模組。許多流行的庫,例如 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-rangemoment 物件添加了一個新的 range 方法。

就編寫宣告檔案而言,無論所改變的模組是普通模組還是 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+

最後更新:2026 年 3 月 27 日