TypeScript 模块与命名空间
这一篇聊聊 TypeScript 的模块系统:ES 模块、命名空间、声明合并,
以及模块解析策略。
ES 模块
导出(Export)
1// 命名导出
2export const name = 'TypeScript';
3export function greet() {
4 return 'Hello';
5}
6export class User {}
7
8// 导出声明
9const age = 30;
10export { age };
11
12// 重命名导出
13export { age as userAge };
14
15// 默认导出
16export default class MyClass {}
17
18// 导出所有
19export * from './other-module';
20export { specific } from './other-module';
导入(Import)
1// 命名导入
2import { name, greet, User } from './module';
3
4// 重命名导入
5import { name as moduleName } from './module';
6
7// 默认导入
8import MyClass from './module';
9
10// 命名空间导入
11import * as Module from './module';
12
13// 混合导入
14import MyClass, { name, greet } from './module';
15
16// 仅导入类型
17import type { User } from './module';
18import { type User, type Admin } from './module';
19
20// 导入并重新导出
21export { name } from './module';
动态导入
1// 动态导入返回 Promise
2async function loadModule() {
3 const module = await import('./module');
4 module.greet();
5}
6
7// 条件导入
8if (condition) {
9 const module = await import('./module');
10}
命名空间(Namespaces)
命名空间定义
1namespace Validation {
2 export interface StringValidator {
3 isAcceptable(s: string): boolean;
4 }
5
6 const lettersRegexp = /^[A-Za-z]+$/;
7 const numberRegexp = /^[0-9]+$/;
8
9 export class LettersOnlyValidator implements StringValidator {
10 isAcceptable(s: string) {
11 return lettersRegexp.test(s);
12 }
13 }
14
15 export class ZipCodeValidator implements StringValidator {
16 isAcceptable(s: string) {
17 return s.length === 5 && numberRegexp.test(s);
18 }
19 }
20}
21
22// 使用
23let validators: { [s: string]: Validation.StringValidator } = {};
24validators['ZIP code'] = new Validation.ZipCodeValidator();
25validators['Letters only'] = new Validation.LettersOnlyValidator();
多文件命名空间
1// Validation.ts
2namespace Validation {
3 export interface StringValidator {
4 isAcceptable(s: string): boolean;
5 }
6}
7
8// LettersOnlyValidator.ts
9/// <reference path="Validation.ts" />
10namespace Validation {
11 const lettersRegexp = /^[A-Za-z]+$/;
12 export class LettersOnlyValidator implements StringValidator {
13 isAcceptable(s: string) {
14 return lettersRegexp.test(s);
15 }
16 }
17}
18
19// ZipCodeValidator.ts
20/// <reference path="Validation.ts" />
21namespace Validation {
22 const numberRegexp = /^[0-9]+$/;
23 export class ZipCodeValidator implements StringValidator {
24 isAcceptable(s: string) {
25 return s.length === 5 && numberRegexp.test(s);
26 }
27 }
28}
命名空间别名
1namespace Shapes {
2 export namespace Polygons {
3 export class Triangle {}
4 export class Square {}
5 }
6}
7
8import polygons = Shapes.Polygons;
9let sq = new polygons.Square();
声明合并(Declaration Merging)
接口合并
1interface Box {
2 height: number;
3 width: number;
4}
5
6interface Box {
7 scale: number;
8}
9
10// 两个声明会合并
11let box: Box = {
12 height: 5,
13 width: 6,
14 scale: 10
15};
命名空间合并
1namespace Animals {
2 export class Zebra {}
3}
4
5namespace Animals {
6 export interface Legged {
7 numberOfLegs: number;
8 }
9 export class Dog {}
10}
11
12// 合并后包含 Zebra、Legged、Dog
命名空间与类合并
1class Album {
2 label: Album.AlbumLabel;
3}
4
5namespace Album {
6 export class AlbumLabel {}
7}
8
9// Album 类有 label 属性,类型是 Album.AlbumLabel
命名空间与函数合并
1function buildLabel(name: string): string {
2 return buildLabel.prefix + name + buildLabel.suffix;
3}
4
5namespace buildLabel {
6 export let suffix = '';
7 export let prefix = 'Hello, ';
8}
9
10// buildLabel 函数有 prefix 和 suffix 属性
命名空间与枚举合并
1enum Color {
2 red = 1,
3 green = 2,
4 blue = 4
5}
6
7namespace Color {
8 export function mixColor(colorName: string) {
9 if (colorName == 'red') {
10 return Color.red;
11 } else if (colorName == 'green') {
12 return Color.green;
13 } else if (colorName == 'blue') {
14 return Color.blue;
15 }
16 }
17}
18
19// Color 枚举有 mixColor 方法
模块解析
模块解析策略
TypeScript 支持两种模块解析策略:
- classic:TypeScript 的默认策略(已废弃)
- node:Node.js 的模块解析策略(推荐)
1{
2 "compilerOptions": {
3 "moduleResolution": "node"
4 }
5}
Node 模块解析
Node.js 模块解析规则:
- 相对路径:
./module、../module - 绝对路径:
/module - 非相对路径:从
node_modules查找
1// 相对路径
2import { something } from './module';
3import { something } from '../module';
4
5// 非相对路径
6import { something } from 'module';
7import { something } from 'module/submodule';
路径映射
使用 paths 配置路径别名:
1{
2 "compilerOptions": {
3 "baseUrl": ".",
4 "paths": {
5 "@/*": ["src/*"],
6 "utils/*": ["src/utils/*"]
7 }
8 }
9}
1// 使用路径别名
2import { something } from '@/utils/helper';
3import { something } from 'utils/helper';
类型声明文件查找
TypeScript 会按以下顺序查找类型声明:
package.json中的types字段@types/*包- 同名的
.d.ts文件
1{
2 "types": "./dist/index.d.ts"
3}
三斜线指令
///
引用另一个文件:
1/// <reference path="Validation.ts" />
2namespace Validation {
3 // 可以使用 Validation.ts 中的类型
4}
///
引用类型声明包:
1/// <reference types="node" />
///
引用内置库:
1/// <reference lib="es2015" />
模块 vs 命名空间
使用模块当:
- 需要 ES 模块支持
- 需要 tree-shaking
- 需要动态导入
- 现代项目推荐使用
1// 模块
2export class User {}
3export function greet() {}
使用命名空间当:
- 需要声明合并
- 需要组织相关代码
- 需要向后兼容
- 不推荐在新项目中使用
1// 命名空间
2namespace MyApp {
3 export class User {}
4 export function greet() {}
5}
实际应用场景
类型声明文件
1// types/global.d.ts
2declare global {
3 interface Window {
4 myCustomProperty: string;
5 }
6}
7
8export {};
模块扩展
1// 扩展第三方模块
2declare module 'module-name' {
3 export interface ExtendedType {
4 newProperty: string;
5 }
6}
环境声明
1// 声明全局变量
2declare const API_URL: string;
3declare function myGlobalFunction(): void;
小结
TypeScript 的模块和命名空间系统:
- ES 模块:现代标准,支持导入导出、动态导入
- 命名空间:组织代码的方式,支持声明合并
- 声明合并:接口、命名空间、类、函数、枚举都可以合并
- 模块解析:Node.js 风格的模块解析,支持路径映射
- 三斜线指令:引用其他文件和类型声明
在实际开发中:
- 优先使用 ES 模块,而不是命名空间
- 使用声明合并扩展第三方类型
- 配置路径别名简化导入路径
- 理解模块解析规则,避免路径问题