关于术语的一点说明: 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,这是为了与 ECMAScript 2015里的术语保持一致,(也就是说 module X { 相当于现在推荐的写法 namespace X {)。
- 从ECMAScript 2015开始,JavaScript引入了模块的概念。TypeScript也沿用这个概念。
TypeScript与ECMAScript 2015一样,任何包含顶级import
或者export
的文件都被当成一个模块。相反地,如果一个文件不带有顶级的import
或者export
声明,那么它的内容被视为全局可见的(因此对模块也是可见的)。
TypeScript 模块是组织代码和实现代码重用的重要工具。模块帮助将代码分隔成逻辑单元,并允许这些单元之间进行清晰的依赖管理和代码共享。下面详细介绍 TypeScript 模块的概念、使用方法以及各种高级特性。
1. 模块基础
模块的定义:在 TypeScript 中,一个文件就是一个模块。模块中的代码默认是私有的,只有通过导出(export)的内容才能在其他模块中使用。
基本用法:
导出和导入
导出 (Export):可以通过 export
关键字将变量、函数、类、接口等导出。
// math.ts
export const pi = 3.14;
export function add(a: number, b: number): number {
return a + b;
}
export class Circle {
constructor(public radius: number) {}
area() {
return pi * this.radius * this.radius;
}
}
导入 (Import):可以通过 import
关键字从其他模块中导入导出的内容。
// main.ts
import { pi, add, Circle } from './math';
console.log(`Value of pi: ${pi}`);
console.log(`Add 2 + 3: ${add(2, 3)}`);
const myCircle = new Circle(5);
console.log(`Area of circle: ${myCircle.area()}`);
2. 默认导出(Default Exports)
默认导出是指一个模块可以有一个默认导出,使用 export default
语法。
// greet.ts
export default function greet(name: string): string {
return `Hello, ${name}!`;
}
// main.ts
import greet from './greet';
console.log(greet("World")); // "Hello, World!"
说明:默认导出的内容在导入时不需要使用花括号 {}
,并且可以使用任意名字进行导入。
3. 重命名导入和导出
可以在导出和导入时对标识符进行重命名,以避免命名冲突或提升代码可读性。
导出时重命名:
// constants.ts
const piValue = 3.14;
export { piValue as pi };
导入时重命名:
// main.ts
import { pi as PI } from './constants';
console.log(PI); // 3.14
4. 重新导出(Re-Exporting)
重新导出可以将一个模块的所有内容或部分内容重新导出,以便在其他模块中使用。
重新导出所有内容:
// allMath.ts
export * from './math';
重新导出部分内容:
// someMath.ts
export { pi, add } from './math';
5. 动态导入
动态导入允许在运行时按需加载模块,使用 import()
语法。
// main.ts
async function loadModule() {
const math = await import('./math');
console.log(math.add(2, 3)); // 5
}
loadModule();
说明:动态导入返回一个 Promise
,适合用于按需加载、代码分割和性能优化。
6. 命名空间(Namespaces)
命名空间用于将一组相关的代码组织在一起,它是 TypeScript 提供的另一种模块化方式,但在现代开发中更推荐使用模块(ES 模块)。
namespace MathUtils {
export const pi = 3.14;
export function add(a: number, b: number): number {
return a + b;
}
export class Circle {
constructor(public radius: number) {}
area() {
return pi * this.radius * this.radius;
}
}
}
// 使用命名空间
console.log(MathUtils.pi); // 3.14
console.log(MathUtils.add(2, 3)); // 5
const circle = new MathUtils.Circle(5);
console.log(circle.area()); // 78.5
7. 模块解析
TypeScript 提供了两种模块解析策略:经典(Classic) 和 节点(Node)。默认情况下,TypeScript 使用节点解析策略。
节点解析策略:遵循 Node.js 的模块解析逻辑,支持相对路径和包路径导入。
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node"
}
}
8. 配置模块
在 tsconfig.json
文件中,可以通过 compilerOptions
配置模块相关选项。
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@utils/*": ["src/utils/*"]
}
}
}
说明:通过 baseUrl
和 paths
选项,可以配置路径别名,简化模块导入路径。
9. 高级用法示例
结合以上各种模块特性,可以实现复杂的模块组织和依赖管理。
示例:
// utils.ts
export function log(message: string) {
console.log(message);
}
// math.ts
import { log } from './utils';
export const pi = 3.14;
export function add(a: number, b: number): number {
log(`Adding ${a} and ${b}`);
return a + b;
}
// circle.ts
import { pi } from './math';
export class Circle {
constructor(public radius: number) {}
area() {
return pi * this.radius * this.radius;
}
}
// main.ts
import { add } from './math';
import { Circle } from './circle';
console.log(add(2, 3)); // "Adding 2 and 3" 5
const circle = new Circle(5);
console.log(circle.area()); // 78.5
总结
TypeScript 模块通过导出和导入机制帮助开发者将代码组织成逻辑单元,提高代码可维护性和重用性。默认导出、重命名导入导出、动态导入、命名空间等高级特性使得模块的使用更加灵活和强大。通过合理配置和使用这些特性,可以构建清晰、模块化和高效的 TypeScript 项目。