這篇文章概述了使用 TypeScript 中的模組和命名空間組織程式碼的各種方式。我們還將探討如何使用命名空間和模組的一些進階主題,並說明在 TypeScript 中使用它們時常見的一些陷阱。
請參閱 模組 文件以取得有關 ES 模組的更多資訊。請參閱 命名空間 文件以取得有關 TypeScript 命名空間的更多資訊。
注意:在非常舊版本的 TypeScript 中,命名空間稱為「內部模組」,這些模組早於 JavaScript 模組系統。
使用模組
模組可以包含程式碼和宣告。
模組也依賴於模組載入器 (例如 CommonJs/Require.js) 或支援 ES 模組的執行時期。模組提供更好的程式碼重複使用、更強的隔離和更好的工具支援,以進行綑綁。
值得注意的是,對於 Node.js 應用程式,模組是預設值,我們建議在現代程式碼中使用模組,而不是命名空間。
從 ECMAScript 2015 開始,模組是語言的原生部分,並且應由所有相容的引擎實作支援。因此,對於新專案,模組將是建議的程式碼組織機制。
使用命名空間
命名空間是 TypeScript 特有的程式碼組織方式。
命名空間只是在全域命名空間中的命名 JavaScript 物件。這使得命名空間成為一個非常簡單的建構體。與模組不同,它們可以跨越多個檔案,並且可以使用 outFile
進行串接。命名空間可以是結構化網頁應用程式程式碼的好方法,其中所有相依性都包含在 HTML 頁面中的 <script>
標籤中。
就像所有全域命名空間污染一樣,很難識別組件相依性,特別是在大型應用程式中。
命名空間和模組的缺點
在本節中,我們將描述使用命名空間和模組的各種常見缺點,以及如何避免它們。
/// <reference>
-ing 一個模組
一個常見的錯誤是嘗試使用 /// <reference ... />
語法來參考一個模組檔案,而不是使用 import
陳述式。為了了解區別,我們首先需要了解編譯器如何根據 import
路徑(例如 import x from "...";
、import x = require("...");
等中的 ...
)來找到模組的類型資訊。
編譯器將嘗試尋找具有適當路徑的 .ts
、.tsx
,然後是 .d.ts
。如果找不到特定檔案,則編譯器將尋找環境模組宣告。請記住,這些需要在 .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
tsexport namespace Shapes {export class Triangle {/* ... */}export class Square {/* ... */}}
此處的頂層命名空間 Shapes
無緣無故地包裝了 Triangle
和 Square
。這對於模組的使用者來說很混亂且令人討厭
-
shapeConsumer.ts
tsimport * as shapes from "./shapes";let t = new shapes.Shapes.Triangle(); // shapes.Shapes?
TypeScript 中模組的一個關鍵功能是兩個不同的模組永遠不會將名稱貢獻給同一個範圍。由於模組的使用者決定要指定什麼名稱,因此不需要主動將匯出的符號包裝在命名空間中。
為了重申為什麼您不應該嘗試對模組內容命名空間,命名空間的一般概念是提供建構的邏輯分組並防止名稱衝突。由於模組檔案本身已經是一個邏輯分組,並且其頂層名稱是由匯入它的程式碼定義的,因此不需要對匯出的物件使用額外的模組層。
以下是修改後的範例
-
shapes.ts
tsexport class Triangle {/* ... */}export class Square {/* ... */} -
shapeConsumer.ts
tsimport * as shapes from "./shapes";let t = new shapes.Triangle();
模組的權衡
就像 JS 檔案與模組之間是一對一的對應關係,TypeScript 的模組原始檔與其發出的 JS 檔案之間也是一對一的對應關係。這會造成一個影響,就是無法串接多個模組原始檔,具體取決於你鎖定的模組系統。例如,無法在鎖定 `commonjs` 或 `umd` 的同時使用 outFile
選項,但使用 TypeScript 1.8 及更新版本時,可以 在鎖定 `amd` 或 `system` 時使用 outFile
。