模組

JavaScript 在處理程式碼模組化方面有著悠久的歷史。TypeScript 自 2012 年問世以來,已經實現了對其中許多格式的支援,但隨著時間的推移,社群和 JavaScript 規範已收斂於一種稱為 ES Modules(或 ES6 模組)的格式。你可能透過 import/export 語法瞭解它。

ES Modules 於 2015 年被新增到 JavaScript 規範中,到 2020 年,大多數 Web 瀏覽器和 JavaScript 執行時都已經廣泛支援它。

為了集中精力,本手冊將涵蓋 ES Modules 及其流行的前身 CommonJS 的 module.exports = 語法。你可以在參考部分的模組 (Modules)頁面中找到關於其他模組模式的資訊。

JavaScript 模組的定義方式

在 TypeScript 中,正如在 ECMAScript 2015 中一樣,任何包含頂級 importexport 的檔案都被視為模組。

相反,沒有頂級 importexport 宣告的檔案會被視為指令碼,其內容在全域性作用域內可用(因此模組也可以訪問)。

模組在它們自己的作用域內執行,而不是在全域性作用域內。這意味著除非顯式地使用某種匯出形式將變數、函式、類等匯出,否則它們在模組外部是不可見的。同樣,要使用從其他模組匯出的變數、函式、類、介面等,必須使用某種匯入形式將其匯入。

非模組

在開始之前,理解 TypeScript 如何定義一個模組非常重要。JavaScript 規範宣告,任何沒有 import 宣告、export 或頂級 await 的 JavaScript 檔案都應被視為指令碼,而不是模組。

在指令碼檔案中,變數和型別被宣告在共享的全域性作用域中,並且通常假設你將使用 outFile 編譯器選項將多個輸入檔案合併為一個輸出檔案,或者在 HTML 中使用多個 <script> 標籤來載入這些檔案(且順序必須正確!)。

如果你有一個當前沒有任何 importexport 的檔案,但你想讓它被視為一個模組,請新增一行:

ts
export {};
Try

這會將該檔案更改為一個不匯出任何內容的模組。此語法適用於任何模組目標。

TypeScript 中的模組

延伸閱讀
Impatient JS (模組)
MDN: JavaScript 模組

在 TypeScript 中編寫基於模組的程式碼時,有三個主要方面需要考慮:

  • 語法:我想要使用什麼語法來匯入和匯出內容?
  • 模組解析:模組名稱(或路徑)與磁碟上的檔案之間是什麼關係?
  • 模組輸出目標:編譯出的 JavaScript 模組應該是什麼樣子?

ES 模組語法

檔案可以透過 export default 宣告一個主匯出。

ts
// @filename: hello.ts
export default function helloWorld() {
console.log("Hello, world!");
}
Try

然後透過以下方式匯入:

ts
import helloWorld from "./hello.js";
helloWorld();
Try

除了預設匯出之外,還可以透過省略 default 關鍵字,使用 export 匯出多個變數和函式。

ts
// @filename: maths.ts
export var pi = 3.14;
export let squareTwo = 1.41;
export const phi = 1.61;
 
export class RandomNumberGenerator {}
 
export function absolute(num: number) {
if (num < 0) return num * -1;
return num;
}
Try

這些可以在另一個檔案中使用 import 語法來使用:

ts
import { pi, phi, absolute } from "./maths.js";
 
console.log(pi);
const absPhi = absolute(phi);
const absPhi: number
Try

其他匯入語法

匯入內容可以使用 import {old as new} 格式進行重新命名。

ts
import { pi as π } from "./maths.js";
 
console.log(π);
(alias) var π: number import π
Try

你可以將上述語法混合在一起使用到單個 import 中。

ts
// @filename: maths.ts
export const pi = 3.14;
export default class RandomNumberGenerator {}
 
// @filename: app.ts
import RandomNumberGenerator, { pi as π } from "./maths.js";
 
RandomNumberGenerator;
(alias) class RandomNumberGenerator import RandomNumberGenerator
 
console.log(π);
(alias) const π: 3.14 import π
Try

你可以使用 * as name 將所有匯出的物件放入一個名稱空間中。

ts
// @filename: app.ts
import * as math from "./maths.js";
 
console.log(math.pi);
const positivePhi = math.absolute(math.phi);
const positivePhi: number
Try

你可以匯入一個檔案,並透過 import "./file" 方式在當前模組中包含任何變數。

ts
// @filename: app.ts
import "./maths.js";
 
console.log("3.14");
Try

在這種情況下,import 什麼都不做。然而,maths.ts 中的所有程式碼都會被執行,這可能會觸發影響其他物件的副作用。

TypeScript 特有的 ES 模組語法

型別可以使用與 JavaScript 值相同的語法進行匯出和匯入。

ts
// @filename: animal.ts
export type Cat = { breed: string; yearOfBirth: number };
 
export interface Dog {
breeds: string[];
yearOfBirth: number;
}
 
// @filename: app.ts
import { Cat, Dog } from "./animal.js";
type Animals = Cat | Dog;
Try

TypeScript 擴充套件了 import 語法,增加了兩個用於宣告型別匯入的概念。

import type

這是一種能用於匯入型別的匯入語句。

ts
// @filename: animal.ts
export type Cat = { breed: string; yearOfBirth: number };
export type Dog = { breeds: string[]; yearOfBirth: number };
export const createCatName = () => "fluffy";
 
// @filename: valid.ts
import type { Cat, Dog } from "./animal.js";
export type Animals = Cat | Dog;
 
// @filename: app.ts
import type { createCatName } from "./animal.js";
const name = createCatName();
'createCatName' cannot be used as a value because it was imported using 'import type'.1361'createCatName' cannot be used as a value because it was imported using 'import type'.
Try
內聯 type 匯入

TypeScript 4.5 還允許在個別匯入前新增 type 字首,以指明匯入的引用是一個型別。

ts
// @filename: app.ts
import { createCatName, type Cat, type Dog } from "./animal.js";
 
export type Animals = Cat | Dog;
const name = createCatName();
Try

總之,這些功能允許 Babel、swc 或 esbuild 等非 TypeScript 轉譯器知道哪些匯入是可以安全刪除的。

具有 CommonJS 行為的 ES 模組語法

TypeScript 具有與 CommonJS 和 AMD 的 require 直接對應的 ES 模組語法。在大多數情況下,使用 ES 模組的匯入與這些環境中的 require 相同,但這種語法確保了你的 TypeScript 檔案與 CommonJS 輸出之間的一一對應關係。

ts
import fs = require("fs");
const code = fs.readFileSync("hello.ts", "utf8");
Try

你可以在 模組參考頁面 中瞭解更多關於此語法的資訊。

CommonJS 語法

CommonJS 是 npm 上大多數模組所採用的格式。即使你使用的是上述 ES 模組語法編寫程式碼,簡要了解 CommonJS 語法的工作方式也能幫助你更輕鬆地進行除錯。

匯出

識別符號是透過設定一個名為 module 的全域性物件上的 exports 屬性來匯出的。

ts
function absolute(num: number) {
if (num < 0) return num * -1;
return num;
}
 
module.exports = {
pi: 3.14,
squareTwo: 1.41,
phi: 1.61,
absolute,
};
Try

然後這些檔案可以透過 require 語句匯入:

ts
const maths = require("./maths");
maths.pi;
any
Try

或者你可以使用 JavaScript 中的解構特性來簡化:

ts
const { squareTwo } = require("./maths");
squareTwo;
const squareTwo: any
Try

CommonJS 與 ES 模組的互操作性

在預設匯入和模組名稱空間物件匯入之間的區別方面,CommonJS 和 ES 模組的功能存在差異。TypeScript 提供了一個編譯器標誌來減少這兩套不同約束之間的摩擦,即 esModuleInterop

TypeScript 的模組解析選項

模組解析是將 importrequire 語句中的字串轉換為該字串所指向的檔案的過程。

TypeScript 包含兩種解析策略:Classic 和 Node。Classic 是當編譯器選項 module 不為 commonjs 時的預設策略,旨在保持向後相容性。Node 策略複製了 Node.js 在 CommonJS 模式下的工作方式,並增加了對 .ts.d.ts 的檢查。

有許多 TSConfig 標誌會影響 TypeScript 中的模組策略:moduleResolution, baseUrl, paths, rootDirs

有關這些策略如何工作的全部詳細資訊,你可以查閱 模組解析 參考頁面。

TypeScript 的模組輸出選項

有兩個選項會影響輸出的 JavaScript:

  • target,用於決定哪些 JS 特性進行降級處理(轉換為在舊版 JavaScript 執行時中執行),哪些保持不變。
  • module,用於決定模組之間如何互動的程式碼。

你使用哪個 target 取決於你期望執行 TypeScript 程式碼的 JavaScript 執行時中可用的特性。這可能是:你支援的最舊的 Web 瀏覽器、你預計執行的最低 Node.js 版本,或者可能來自你的執行時環境的獨特約束(例如 Electron)。

模組之間的所有通訊都透過模組載入器進行,編譯器選項 module 決定了使用哪一個。在執行時,模組載入器負責在執行模組之前定位並執行其所有依賴項。

例如,這是一個使用 ES 模組語法的 TypeScript 檔案,展示了 module 的幾個不同選項:

ts
import { valueOfPi } from "./constants.js";
 
export const twoPi = valueOfPi * 2;
Try

ES2020

ts
import { valueOfPi } from "./constants.js";
export const twoPi = valueOfPi * 2;
 
Try

CommonJS

ts
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
const constants_js_1 = require("./constants.js");
exports.twoPi = constants_js_1.valueOfPi * 2;
 
Try

UMD

ts
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./constants.js"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
const constants_js_1 = require("./constants.js");
exports.twoPi = constants_js_1.valueOfPi * 2;
});
 
Try

注意,ES2020 實際上與原始的 index.ts 相同。

你可以在 TSConfig module 參考中檢視所有可用選項及其生成的 JavaScript 程式碼。

TypeScript 名稱空間

TypeScript 有自己的模組格式,稱為 namespaces(名稱空間),它早於 ES 模組標準。這種語法在建立複雜的定義檔案時有很多有用的特性,並且在 DefinitelyTyped 中仍然被積極使用。雖然沒有被棄用,但名稱空間中的大多數特性都已存在於 ES 模組中,我們建議你使用後者以符合 JavaScript 的發展方向。你可以在 名稱空間參考頁面 中瞭解更多關於名稱空間的資訊。

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

此頁面的貢獻者
RCRyan Cavanaugh (52)
OTOrta Therox (7)
MMyo (3)
ABAndrew Branch (2)
HAHossein Ahmadian-Yazdi (2)
18+

最後更新:2026 年 3 月 27 日