强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

TypeScript 开发指南 / 12 - 模块系统

模块系统

TypeScript 的模块系统基于 ECMAScript 模块标准,并扩展了命名空间等特性。

ES 模块语法

命名导出(Named Export)

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

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

export const PI = 3.14159;

export interface MathUtils {
  add: typeof add;
  subtract: typeof subtract;
}

默认导出(Default Export)

// UserService.ts
export default class UserService {
  async getUser(id: number): Promise<User> {
    // ...
  }

  async createUser(data: CreateUserDto): Promise<User> {
    // ...
  }
}

导入语法

// 命名导入
import { add, subtract, PI } from "./utils";

// 重命名导入
import { add as addition } from "./utils";

// 默认导入
import UserService from "./UserService";

// 命名 + 默认混合导入
import UserService, { add, subtract } from "./module";

// 命名空间导入
import * as math from "./utils";
math.add(1, 2);

// 类型导入(TypeScript 特有)
import type { User, UserRole } from "./types";
import { type User, type UserRole, createUser } from "./types";

// 动态导入
const module = await import("./utils");
module.add(1, 2);

type-only 导入

// 使用 import type 只导入类型
import type { User } from "./types";

// 这种导入在编译后会被完全移除
// 优点:
// 1. 避免运行时循环依赖
// 2. 减少编译输出大小
// 3. 明确表明这是纯类型导入

// 内联 type 导入
import { createUser, type User, type UserRole } from "./services/user";

Re-export(重新导出)

// barrel 文件(桶文件)
// index.ts
export { UserService } from "./UserService";
export { PostService } from "./PostService";
export { CommentService } from "./CommentService";

// 重新导出类型
export type { User, UserRole } from "./types";
export type { Post, PostStatus } from "./types";

// 重命名重新导出
export { UserService as User } from "./UserService";

// 从第三方库重新导出
export { default as axios } from "axios";

CommonJS 模块

TypeScript 支持 CommonJS 模块语法:

// 导出
export = UserService;

// 导入(旧语法)
import UserService = require("./UserService");

// 或使用 esModuleInterop
import UserService from "./UserService";

CJS vs ESM 对比

特性CommonJSES Modules
语法require / module.exportsimport / export
加载方式同步、运行时异步、编译时
树摇不支持支持
浏览器不支持原生支持
Node.js默认支持需配置 "type": "module"

命名空间(Namespace)

命名空间是 TypeScript 特有的模块组织方式,主要用于声明文件:

namespace Validation {
  export interface Validator {
    validate(value: string): boolean;
  }

  export class EmailValidator implements Validator {
    validate(value: string): boolean {
      return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
    }
  }

  export class UrlValidator implements Validator {
    validate(value: string): boolean {
      try {
        new URL(value);
        return true;
      } catch {
        return false;
      }
    }
  }
}

// 使用
const emailValidator = new Validation.EmailValidator();
console.log(emailValidator.validate("test@example.com")); // true

命名空间合并

// 可以在多个声明中扩展命名空间
namespace Validation {
  export class PhoneValidator implements Validator {
    validate(value: string): boolean {
      return /^\d{11}$/.test(value);
    }
  }
}

// 后续声明中可以访问之前声明的内容

注意:现代 TypeScript 项目推荐使用 ES 模块,命名空间主要用于 .d.ts 声明文件和全局类型扩展。

模块解析策略

相对路径导入

// 相对导入(推荐用于项目内部模块)
import { User } from "./types";
import { UserService } from "../services/UserService";
import { config } from "../../config";

非相对路径导入(路径别名)

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"],
      "@types/*": ["src/types/*"]
    }
  }
}
// 使用路径别名
import { Button } from "@components/Button";
import { formatDate } from "@utils/date";
import type { User } from "@types/user";

模块解析策略选项

策略说明适用场景
nodeNode.js CommonJS 解析CJS 项目
node16Node.js 16+ 解析Node.js 16+
nodenextNode.js 最新解析Node.js 最新
bundler打包器策略Vite/Webpack/esbuild
// Node.js 项目
{
  "compilerOptions": {
    "module": "Node16",
    "moduleResolution": "Node16"
  }
}

// 打包器项目(Vite/Webpack)
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler"
  }
}

声明文件与模块

.d.ts 声明文件

// types/global.d.ts
// 全局类型扩展

declare global {
  interface Window {
    __APP_VERSION__: string;
    __API_URL__: string;
  }

  // 全局工具类型
  type Nullable<T> = T | null;
  type Optional<T> = T | undefined;
}

export {};

// 使用
console.log(window.__APP_VERSION__);

为没有类型的库添加声明

// types/lodash.d.ts
declare module "lodash" {
  export function chunk<T>(array: T[], size: number): T[][];
  export function groupBy<T>(
    collection: T[],
    iteratee: keyof T | ((value: T) => string)
  ): Record<string, T[]>;
  export function debounce<T extends (...args: any[]) => any>(
    func: T,
    wait?: number
  ): T;
}

// 或者更简单的声明
declare module "some-untyped-lib" {
  const value: any;
  export default value;
}

声明合并

接口合并

// 库的原始声明
interface User {
  name: string;
  age: number;
}

// 扩展声明(自动合并)
interface User {
  email: string;
  avatar?: string;
}

// 最终 User 有所有四个属性
const user: User = {
  name: "Alice",
  age: 25,
  email: "alice@example.com"
};

全局类型扩展

// types/express.d.ts
import { User } from "./models";

declare global {
  namespace Express {
    interface Request {
      user?: User;
      requestId: string;
    }
  }
}

export {};

模块与打包

tree-shaking 友好的导出

// ✅ 好的导出方式(支持 tree-shaking)
export function add(a: number, b: number): number { return a + b; }
export function subtract(a: number, b: number): number { return a - b; }

// ❌ 不好的方式(整个对象都会被打包)
const math = {
  add(a: number, b: number) { return a + b; },
  subtract(a: number, b: number) { return a - b; }
};
export default math;

模块打包配置

// tsconfig.json - 用于打包
{
  "compilerOptions": {
    "module": "ESNext",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}
// package.json
{
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    },
    "./utils": {
      "import": "./dist/utils.mjs",
      "require": "./dist/utils.js",
      "types": "./dist/utils.d.ts"
    }
  }
}

业务场景:插件系统

// types/plugin.ts
export interface Plugin {
  name: string;
  version: string;
  install(app: App): void;
}

export interface App {
  use(plugin: Plugin): void;
  config: AppConfig;
}

export interface AppConfig {
  debug: boolean;
  baseUrl: string;
}

// plugins/analytics.ts
import type { Plugin, App } from "../types/plugin";

export class AnalyticsPlugin implements Plugin {
  name = "analytics";
  version = "1.0.0";

  install(app: App): void {
    // 注册分析功能
  }
}

// plugins/auth.ts
import type { Plugin, App } from "../types/plugin";

export class AuthPlugin implements Plugin {
  name = "auth";
  version = "1.0.0";

  constructor(private options: { tokenKey: string }) {}

  install(app: App): void {
    // 注册认证功能
  }
}

// index.ts(barrel 文件)
export { AnalyticsPlugin } from "./plugins/analytics";
export { AuthPlugin } from "./plugins/auth";
export type { Plugin, App, AppConfig } from "./types/plugin";

注意事项

  1. 优先使用 ES 模块——命名空间主要用于声明文件
  2. 使用 import type——明确区分类型导入和值导入
  3. 路径别名需要打包器配合——paths 只影响 TypeScript 编译,打包器需要额外配置
  4. barrel 文件(index.ts)可以简化导入路径,但注意可能影响 tree-shaking
  5. 声明合并常用于扩展第三方库的类型

扩展阅读