在现代JavaScript/TypeScript开发中,模块系统是实现代码组织、复用与维护的核心机制。通过将代码分割为独立模块,开发者可以实现关注点分离,提升代码的可扩展性与团队协作效率。ES模块(ESM)作为官方标准,已成为TypeScript项目的主流模块方案,其核心价值在于提供了标准化的导出(Export)与导入(Import)语法,支持类型与值的精细化管理。
命名导出允许模块对外暴露多个独立成员,每个成员需显式指定名称。其语法形式包括声明时导出与集中导出两种:
// 声明时导出
export const framework = "TypeScript";
export function log(message: string): void {
console.log(`[TS] ${message}`);
}
// 集中导出(适用于已有声明的成员)
const version = "5.0";
export { version };
对应的导入需使用解构语法指定成员名称,且名称必须与导出端完全一致:
import { framework, log, version } from './constants';
log(`${framework} ${version}`); // 输出 "[TS] TypeScript 5.0"
默认导出用于模块对外暴露单一主要成员,每个模块仅允许一个默认导出。其语法简洁,导入时可自定义成员名称:
// User.ts - 默认导出类
export default class User {
constructor(public name: string) {}
}
// 导入时自定义名称(通常保持与类名一致以增强可读性)
import User from './User';
const user = new User("Alice");
默认导出适用于工具类、组件等具有明确单一职责的模块,但需注意过度使用可能导致导入名称混乱,建议与命名导出配合使用以平衡灵活性与可读性。
TypeScript 5.0引入了显式类型导出机制,通过export type语法明确区分类型与值的导出。这一特性解决了传统导出中类型与值的歧义问题,且类型导出在编译为JavaScript时会被完全移除,避免运行时冗余。
// types.ts - 定义接口类型
interface User {
id: number;
name: string;
}
// 导出类型(仅用于类型系统)
export type { User };
// 导出值(运行时保留)
export const DEFAULT_USER: User = { id: 0, name: "Guest" };
类型导出与普通导出的核心区别在于:
export type仅用于类型(接口、类型别名、类类型等),编译后无残留export可导出值(变量、函数、类实例等),编译后保留在运行时代码中此外,TypeScript支持通过export type * from './types'批量导出其他模块的类型,实现类型的聚合管理:
// index.ts - 汇总类型导出
export type * from './user.types';
export type * from './post.types';
注意:使用import type可显式导入类型(如import type { User } from './types'),进一步强化类型与值的分离,避免类型导入被误用作运行时值。
1. 前端工具函数模块
在前端项目中,工具函数通常按功能拆分并通过索引模块汇总导出,提升导入便捷性:
// math.ts - 导出具体工具函数
export function add(a: number, b: number): number {
return a + b;
}
export function sub(a: number, b: number): number {
return a - b;
}
// index.ts - 汇总导出
export * from './math';
export * from './string';
export * from './date';
// 使用时直接从索引模块导入
import { add, formatDate } from './utils';
2. 后端路由模块
在Node.js后端开发中,路由配置常按资源拆分,通过模块导入实现路由注册的解耦:
// user.routes.ts - 导出路由配置
import { Router } from 'express';
const router = Router();
router.get('/users', (req, res) => res.json([]));
router.post('/users', (req, res) => res.status(201).json({}));
export default router;
// app.ts - 导入并注册路由
import express from 'express';
import userRouter from './routes/user.routes';
const app = express();
app.use('/api', userRouter); // 挂载用户路由
基于上一章实现的“用户类”,按以下步骤进行模块拆分:
// User.ts
export type UserRole = 'admin' | 'user' | 'guest';
export class User {
constructor(
public id: number,
public name: string,
public role: UserRole = 'user'
) {}
isAdmin(): boolean {
return this.role === 'admin';
}
}
// main.ts
import { User, UserRole } from './User';
const admin: User = new User(1, 'Admin', 'admin');
console.log(admin.isAdmin()); // 输出 true
通过模块拆分,代码结构更清晰,User类的复用与维护性显著提升,同时类型与值的显式分离也增强了代码的可读性与安全性。
模块系统作为TypeScript项目的基础架构,其规范的应用直接影响项目的可维护性。合理选择导出方式(命名/默认/类型)、建立清晰的模块层次,是构建可扩展应用的关键前提。后续章节将进一步探讨模块解析策略、路径别名配置等高级应用。