模块(Modules) 是现代 TypeScript 项目中组织和管理代码的最主要方式。它基于 ES6 的 import 和 export 语法(也称为 ES Modules 或 ESM),完全取代了旧的命名空间(namespace)。模块系统让代码具备作用域隔离、按需加载、tree-shaking 等优势,是当前所有主流框架(React、Vue、Angular、NestJS、Next.js 等)的标准做法。
1. 基本导出与导入
a. 导出(export)
// file: utils.ts
export function sum(a: number, b: number): number {
return a + b;
}
export const PI = 3.14159;
export interface User {
name: string;
age: number;
}
export class Calculator {
multiply(x: number, y: number): number {
return x * y;
}
}
// 默认导出(一个模块只能有一个)
export default function greet(name: string): string {
return `Hello, ${name}!`;
}
b. 导入(import)
// file: main.ts
import greet from "./utils"; // 默认导入
import { sum, PI, User, Calculator } from "./utils"; // 命名导入
import { sum as add } from "./utils"; // 重命名
import * as Utils from "./utils"; // 导入所有为命名空间对象
greet("Alice"); // "Hello, Alice!"
console.log(sum(2, 3)); // 5
console.log(Utils.PI);
let calc = new Calculator();
calc.multiply(4, 5);
2. 导出方式总结
| 方式 | 语法示例 | 说明 |
|---|---|---|
| 命名导出 | export function fn() {} | 可导出多个 |
| 默认导出 | export default class MyClass {} | 一个模块只能一个 |
| 重新导出(Re-export) | export { name } from "./other"; | 聚合模块 |
| 全部重新导出 | export * from "./utils"; | 导出其他模块的所有(不包括默认导出) |
| 重命名导出 | export { sum as add } from "./math"; |
3. 模块路径与解析
- 相对路径:
"./utils"、"../models/user" - 绝对路径(需配置
baseUrl和paths):
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src", // 根目录
"paths": {
"@utils/*": ["utils/*"], // 别名
"@components/*": ["components/*"]
}
}
}
使用:
import { sum } from "@utils/math";
import Button from "@components/Button";
4. 模块模式(Module Mode)
在 tsconfig.json 中配置:
| 配置值 | 输出格式 | 适用环境 |
|---|---|---|
"ESNext" 或 "ES2022" | 原生 ES Modules | 现代浏览器、Vite、Deno、Bun |
"CommonJS" | require/module.exports | Node.js(传统) |
"AMD" / "UMD" / "System" | 旧模块系统 | 老项目 |
推荐:现代项目统一使用 "ESNext" + "moduleResolution": "node" 或 "nodenext"。
5. 动态导入(Dynamic Import)—— 按需加载
返回 Promise,适合代码分割、懒加载:
// 静态导入(打包时一起加载)
import { heavyFunction } from "./heavy";
// 动态导入(运行时加载)
async function loadHeavy() {
const module = await import("./heavy");
module.heavyFunction();
}
button.addEventListener("click", loadHeavy);
6. 类型声明模块(Declaration Files)
为非 TS 文件(如 .js、第三方库)提供类型:
// file: declarations/jquery.d.ts
declare module "jquery" {
export default function $(selector: string): any;
}
// 使用
import $ from "jquery";
$("#app").html("Hello");
或全局声明:
// file: globals.d.ts
declare global {
interface Window {
myGlobalVar: string;
}
}
7. 侧边模块增强(Module Augmentation)
扩展第三方库的类型(常见于 lodash、express 等):
// file: types/express.d.ts
import "express";
declare module "express" {
interface Request {
user?: { id: number; name: string };
}
}
// 现在所有 express Request 都有 user 属性类型提示
8. 常见模块使用场景
| 场景 | 推荐方式 |
|---|---|
| 工具函数 | 一个文件导出多个函数 |
| React 组件 | export default function Component() |
| 类型/接口 | 单独文件导出多个 interface/type |
| 常量配置 | export const CONFIG = { ... } |
| 聚合导出(barrel) | index.ts 中 export * from "./xxx" |
| Node.js 服务端 | "module": "ESNext" + .mjs 或 "type": "module" |
9. 最佳实践建议
| 建议 | 说明 |
|---|---|
| 一个文件一个责任 | 每个文件导出相关的一组内容 |
| 优先默认导出组件 | React/Vue 等组件用 export default |
| 类型单独文件 | interfaces/types 放 types/ 或单独 .d.ts |
| 使用路径别名 | 配置 @utils/* 等,提高可读性 |
| 避免循环依赖 | 模块相互 import 会导致问题 |
| barrel 文件谨慎使用 | export * from 过多影响 tree-shaking |
开启 "isolatedModules": true | 确保兼容 Babel、esbuild 等工具 |
小结:模块 vs 命名空间
| 特性 | 模块 (import/export) | 命名空间 (namespace) |
|---|---|---|
| 现代推荐 | 强烈推荐 | 已过时 |
| 作用域 | 文件级隔离 | 全局或嵌套 |
| 加载方式 | 静态/动态,按需 | 脚本加载 |
| tree-shaking | 支持 | 不支持 |
| 声明文件 | 支持 | 常用于旧库 |
结论:在 2025 年的 TypeScript 开发中,所有新项目都应使用 ES 模块系统。命名空间仅用于维护旧代码或特定声明文件场景。
如果您想看实际项目结构示例(如 React + Vite 的模块组织、Next.js 的页面模块、Node.js 的服务模块),或者想了解如何配置 tsconfig 支持模块,请告诉我!