名稱空間和模組

本文概述了在 TypeScript 中使用模組(Modules)和名稱空間(Namespaces)組織程式碼的各種方式。我們還將探討有關如何使用名稱空間和模組的一些進階主題,並解決在 TypeScript 中使用它們時常見的一些缺陷。

有關 ES 模組的更多資訊,請參閱模組(Modules)文件。有關 TypeScript 名稱空間的更多資訊,請參閱名稱空間(Namespaces)文件。

注意:在非常早期的 TypeScript 版本中,名稱空間被稱為“內部模組”(Internal Modules),它們出現的時間早於 JavaScript 的模組系統。

使用模組

模組可以同時包含程式碼和宣告。

模組還依賴於模組載入器(如 CommonJs/Require.js)或支援 ES 模組的執行時。模組提供了更好的程式碼複用性、更強的隔離性以及更好的打包工具支援。

值得注意的是,對於 Node.js 應用程式,模組是預設選項,並且我們建議在現代程式碼中使用模組而非名稱空間

從 ECMAScript 2015 開始,模組已成為語言的原生組成部分,並且應該得到所有相容引擎實現的支援。因此,對於新專案,模組是推薦的程式碼組織機制。

使用名稱空間

名稱空間是 TypeScript 特有的一種組織程式碼的方式。
名稱空間本質上只是全域性名稱空間中具名的 JavaScript 物件。這使得名稱空間成為一種非常易於使用的結構。與模組不同,它們可以跨越多個檔案,並可以使用 outFile 進行合併。對於 Web 應用程式,如果所有依賴項都以 <script> 標籤的形式包含在 HTML 頁面中,名稱空間是一種組織程式碼的好方法。

和所有全域性名稱空間汙染一樣,在大型應用程式中識別元件依賴關係可能會變得困難。

名稱空間與模組的常見缺陷

在本節中,我們將描述使用名稱空間和模組時常見的各種缺陷,以及如何避免它們。

對模組使用 /// <reference>

一個常見的錯誤是嘗試使用 /// <reference ... /> 語法來引用模組檔案,而不是使用 import 語句。要理解這種區別,我們首先需要了解編譯器如何根據 import 的路徑(例如 import x from "...";import x = require("..."); 等中的 ...)來定位模組的型別資訊。

編譯器會嘗試按照路徑順序查詢 .ts.tsx,然後是 .d.ts 檔案。如果找不到特定檔案,編譯器會查詢環境模組宣告(ambient module declaration)。請記住,這些宣告需要在 .d.ts 檔案中定義。

  • myModules.d.ts

    ts
    // In a .d.ts file or .ts file that is not a module:
    declare module "SomeModule" {
    export function fn(): string;
    }
  • myOtherModule.ts

    ts
    /// <reference path="myModules.d.ts" />
    import * as m from "SomeModule";

這裡的引用標籤允許我們定位包含環境模組宣告的宣告檔案。這就是許多 TypeScript 示例所使用的 node.d.ts 檔案的引用方式。

不必要的名稱空間

如果您正在將程式從名稱空間轉換為模組,很容易得到類似這樣的檔案:

  • shapes.ts

    ts
    export namespace Shapes {
    export class Triangle {
    /* ... */
    }
    export class Square {
    /* ... */
    }
    }

這裡的頂層名稱空間 ShapesTriangleSquare 包裹起來,這是沒有必要的。這對模組的使用者來說既令人困惑又很麻煩。

  • shapeConsumer.ts

    ts
    import * as shapes from "./shapes";
    let t = new shapes.Shapes.Triangle(); // shapes.Shapes?

TypeScript 模組的一個關鍵特性是:兩個不同的模組絕不會將名稱貢獻給同一個作用域。因為模組的使用者決定了給它分配什麼名稱,所以沒有必要主動地將匯出的符號包裹在名稱空間中。

再次強調為什麼不應該嘗試對模組內容進行名稱空間處理:名稱空間的一般目的是提供結構的邏輯分組並防止命名衝突。因為模組檔案本身已經是一個邏輯分組,並且其頂層名稱由匯入它的程式碼定義,因此無需為匯出的物件額外新增一個模組層。

以下是修改後的示例

  • shapes.ts

    ts
    export class Triangle {
    /* ... */
    }
    export class Square {
    /* ... */
    }
  • shapeConsumer.ts

    ts
    import * as shapes from "./shapes";
    let t = new shapes.Triangle();

模組的權衡

正如 JS 檔案與模組之間存在一一對應關係一樣,TypeScript 在模組原始檔與其編譯後的 JS 檔案之間也存在一一對應關係。這意味著根據您所使用的模組系統,無法將多個模組原始檔合併在一起。例如,在以 commonjsumd 為目標時,不能使用 outFile 選項;但從 TypeScript 1.8 開始,在以 amdsystem 為目標時,可以使用 outFile

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

此頁面的貢獻者
DRDaniel Rosenwasser (63)
OTOrta Therox (19)
MHMohamed Hegazy (19)
BBohdan (2)
MFMartin Fischer (1)
15+

最後更新:2026 年 3 月 27 日