庫結構

概括地說,你編寫宣告檔案的結構方式取決於庫的消費(使用)方式。在 JavaScript 中提供庫以供使用的方式有很多,你需要讓你的宣告檔案與之匹配。本指南涵蓋了如何識別常見的庫模式,以及如何編寫與這些模式相對應的宣告檔案。

每種主要的庫結構模式在模板章節都有對應的檔案。你可以從這些模板開始,以幫助你更快地入門。

識別庫的型別

首先,我們將回顧 TypeScript 宣告檔案可以表示的庫型別。我們將簡要展示每種庫是如何被使用的、它是如何被編寫的,並列出一些現實世界中的庫示例。

識別庫的結構是編寫其宣告檔案的第一步。我們將給出基於使用方式程式碼內容來識別結構的提示。根據庫的文件和組織方式,其中一種可能比另一種更容易。我們建議使用你覺得更舒服的方法。

你應該尋找什麼?

在檢視你試圖為其編寫型別定義的庫時,需要問自己的問題。

  1. 你如何獲取該庫?

    例如,你是否只能透過 npm 獲取它,還是隻能從 CDN 獲取?

  2. 你將如何匯入它?

    它是否添加了一個全域性物件?它是否使用了 requireimport/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 模組的文件中看到這些示例,因此請務必檢視程式碼或文件。

從程式碼中識別模組庫

模組化庫通常至少包含以下內容中的一部分

  • 無條件呼叫 requiredefine
  • 類似 import * as a from 'b';export c; 的宣告
  • exportsmodule.exports 的賦值

它們很少有

  • windowglobal 屬性的賦值

模組模板

有四個適用於模組的模板可用:module.d.tsmodule-class.d.tsmodule-function.d.tsmodule-plugin.d.ts

你應該首先閱讀 module.d.ts,以瞭解它們的工作原理。

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

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

如果你的模組可以使用 new 進行構造,則使用模板 module-class.d.ts

js
const x = require("bar");
// Note: using 'new' operator on the imported variable
const 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
// Web
window.createGreeting = function (s) {
return "Hello, " + s;
};
// Node
global.createGreeting = function (s) {
return "Hello, " + s;
};
// Potentially any runtime
globalThis.createGreeting = function (s) {
return "Hello, " + s;
};

在檢視全域性庫程式碼時,你通常會看到

  • 頂級的 var 語句或 function 宣告
  • 一個或多個對 window.someName 的賦值
  • 假設存在像 documentwindow 這樣的 DOM 原生物件

不會看到

  • 對模組載入器(如 requiredefine)的檢查或使用
  • var fs = require("fs"); 這樣的 CommonJS/Node.js 風格的匯入
  • 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 definetypeof windowtypeof module 的測試(尤其是在檔案頂部),那它幾乎肯定是一個 UMD 庫。

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

UMD 庫示例

大多數流行的庫現在都以 UMD 包的形式提供。示例包括 jQueryMoment.jslodash 等等。

模板

使用 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-level
interface CatsKittySettings {}

該指南還確保庫可以在不破壞宣告檔案使用者的情況下過渡到 UMD。

ES6 對模組呼叫簽名的影響

許多流行的庫(如 Express)在匯入時將自己作為可呼叫函式公開。例如,典型的 Express 用法如下

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

在相容 ES6 的模組載入器中,頂層物件(此處匯入為 exp)只能具有屬性;頂層模組物件絕不能是可呼叫的。

這裡最常見的解決方案是為可呼叫/可構造物件定義一個 default 匯出;模組載入器通常會自動檢測這種情況,並用 default 匯出替換頂層物件。如果你在 tsconfig.json 中配置了 "esModuleInterop": true,TypeScript 可以為你處理這個問題。

TypeScript 文件是一個開源專案。透過提交 Pull Request 幫助我們改進這些頁面 傳送 Pull Request

此頁面的貢獻者
MHMohamed Hegazy (57)
OTOrta Therox (17)
DRDaniel Rose (3)
Llilichao (1)
MFMartin Fischer (1)
12+

最後更新:2026 年 3 月 27 日