Skip to content

模块系统概述

概述

TypeScript 支持多种模块系统,主要包括 ES 模块(ES Modules)CommonJS 模块。模块系统允许我们将代码组织成独立的、可重用的单元,通过导入(import)和导出(export)机制实现代码的模块化管理。理解模块系统是构建大型 TypeScript 应用的基础。

模块系统类型

ES 模块(ES Modules)

ES 模块是 JavaScript 的官方模块标准,使用 importexport 关键字。这是现代 TypeScript 项目推荐使用的模块系统。

typescript
// math.ts - 导出模块
export function add(a: number, b: number): number {
  return a + b;
}

export function subtract(a: number, b: number): number {
  return a - b;
}

// 默认导出
export default function multiply(a: number, b: number): number {
  return a * b;
}
typescript
// app.ts - 导入模块
import { add, subtract } from './math';
import multiply from './math'; // 默认导入

const result1 = add(5, 3);      // 8
const result2 = subtract(10, 4); // 6
const result3 = multiply(2, 4);  // 8

CommonJS 模块

CommonJS 是 Node.js 使用的模块系统,使用 requiremodule.exports。TypeScript 也支持 CommonJS 语法。

typescript
// utils.ts - CommonJS 导出
function greet(name: string): string {
  return `Hello, ${name}!`;
}

module.exports = {
  greet
};

// 或者使用 exports
exports.greet = greet;
typescript
// app.ts - CommonJS 导入
const { greet } = require('./utils');

const message = greet('TypeScript'); // "Hello, TypeScript!"

提示

在现代 TypeScript 项目中,推荐使用 ES 模块语法,即使编译目标是 CommonJS,也可以使用 import/export 语法,TypeScript 编译器会自动转换。

模块的基本概念

模块的作用域

每个模块都有自己的作用域,模块内的变量、函数、类等默认是私有的,只有通过 export 导出的内容才能被其他模块访问。

typescript
// user.ts
// 私有变量,外部无法访问
let privateCounter = 0;

// 导出的函数,外部可以访问
export function getUserCount(): number {
  return privateCounter;
}

export function incrementUserCount(): void {
  privateCounter++;
}

// 私有函数,外部无法访问
function validateUser(user: string): boolean {
  return user.length > 0;
}
typescript
// app.ts
import { getUserCount, incrementUserCount } from './user';

incrementUserCount();
console.log(getUserCount()); // 1

// ❌ 错误:无法访问私有变量和函数
// console.log(privateCounter); // 编译错误
// validateUser('test'); // 编译错误

模块的编译目标

TypeScript 编译器可以根据 tsconfig.json 中的 module 选项,将 ES 模块语法编译为不同的模块系统:

json
{
  "compilerOptions": {
    "module": "ES2020",      // 编译为 ES 模块
    "module": "CommonJS",    // 编译为 CommonJS
    "module": "AMD",         // 编译为 AMD
    "module": "UMD"          // 编译为 UMD
  }
}

使用示例

示例 1:创建和使用工具模块

typescript
// utils/string-utils.ts
export function capitalize(str: string): string {
  if (!str) return str;
  return str.charAt(0).toUpperCase() + str.slice(1);
}

export function reverse(str: string): string {
  return str.split('').reverse().join('');
}

export const STRING_CONSTANTS = {
  EMPTY: '',
  SPACE: ' '
} as const;
typescript
// app.ts
import { capitalize, reverse, STRING_CONSTANTS } from './utils/string-utils';

const name = capitalize('typescript'); // "TypeScript"
const reversed = reverse('hello');      // "olleh"
console.log(STRING_CONSTANTS.SPACE);   // " "

示例 2:模块的重新导出

可以使用 export ... from 语法重新导出其他模块的内容,这对于创建统一的模块入口非常有用。

typescript
// math/add.ts
export function add(a: number, b: number): number {
  return a + b;
}

// math/subtract.ts
export function subtract(a: number, b: number): number {
  return a - b;
}

// math/index.ts - 统一导出
export { add } from './add';
export { subtract } from './subtract';

// 也可以导出所有内容
// export * from './add';
// export * from './subtract';
typescript
// app.ts - 从统一入口导入
import { add, subtract } from './math';

const sum = add(1, 2);        // 3
const diff = subtract(5, 2); // 3

示例 3:类型和值的导出

TypeScript 模块可以同时导出类型和值,导入时可以使用 type 关键字明确表示导入的是类型。

typescript
// types.ts
export interface User {
  name: string;
  age: number;
}

export type Status = 'active' | 'inactive';

export class UserService {
  getUser(id: number): User {
    return { name: 'John', age: 30 };
  }
}
typescript
// app.ts
// 导入类型和值
import { User, Status, UserService } from './types';

// 或者明确指定类型导入(推荐,有助于 tree-shaking)
import type { User, Status } from './types';
import { UserService } from './types';

const user: User = { name: 'Alice', age: 25 };
const status: Status = 'active';
const service = new UserService();

模块解析

TypeScript 使用模块解析策略来确定导入语句引用的文件位置。主要有两种策略:

1. Classic 解析策略

传统的解析策略,主要用于向后兼容。

2. Node 解析策略(推荐)

模拟 Node.js 的模块解析机制,这是现代项目的推荐选择。

json
{
  "compilerOptions": {
    "moduleResolution": "node"
  }
}

模块解析会按照以下顺序查找模块:

  1. 相对路径:./module../module
  2. 绝对路径:/path/to/module
  3. Node 模块:从 node_modules 目录查找
  4. 路径映射:根据 tsconfig.json 中的 paths 配置

注意事项

注意

  1. 模块系统选择:根据项目目标环境选择合适的模块系统。浏览器环境通常使用 ES 模块,Node.js 环境可以使用 CommonJS 或 ES 模块。

  2. 循环依赖:避免模块之间的循环依赖,这可能导致运行时错误或未定义的行为。

  3. 默认导出:每个模块只能有一个默认导出,但可以有多个命名导出。

  4. 类型导入:使用 import type 导入类型可以确保这些导入在编译后会被移除,有助于代码优化。

最佳实践

  • 优先使用 ES 模块语法(import/export
  • 使用 import type 导入类型,提高代码清晰度和性能
  • 通过 index.ts 文件创建统一的模块入口
  • 避免使用 export * 导出所有内容,明确导出需要的项

相关链接

基于 VitePress 构建