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 对比
| 特性 | CommonJS | ES Modules |
|---|---|---|
| 语法 | require / module.exports | import / 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";
模块解析策略选项
| 策略 | 说明 | 适用场景 |
|---|---|---|
node | Node.js CommonJS 解析 | CJS 项目 |
node16 | Node.js 16+ 解析 | Node.js 16+ |
nodenext | Node.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";
注意事项
- 优先使用 ES 模块——命名空间主要用于声明文件
- 使用
import type——明确区分类型导入和值导入 - 路径别名需要打包器配合——
paths只影响 TypeScript 编译,打包器需要额外配置 - barrel 文件(index.ts)可以简化导入路径,但注意可能影响 tree-shaking
- 声明合并常用于扩展第三方库的类型