模块系统
# 04.模块系统
# 目录介绍
- 01.模块基本概念
- 02.导出
- 2.1 导出变量、函数、类
- 2.2 默认导出
- 2.3 重新导出
- 2.4 来看一个案例
- 03.导入
- 3.1 导入命名导出
- 3.2 导入默认导出
- 3.3 导入所有内容
- 04.模块解析
- 4.1 相对路径导入
- 4.2 非相对路径导入
- 05.模块的编译
- 06.模块与命名空间
- 6.1 命名空间
- 6.2 避免命名冲突
- 6.3 模块与命名空间结合
- 6.4 命名空间原理
- 6.5 模块VS命名空间
- 6.6 最佳实践建议
- 07.动态导入
- 08.模块类型声明
- 8.1 声明模块
- 8.2 导入类型
- 8.3 模块最佳实践
TypeScript 的模块系统是基于 ES 模块(ES Modules)的,它允许开发者将代码拆分为多个文件,并通过导入和导出的方式组织代码。
TypeScript 的模块系统与 JavaScript 的模块系统兼容,同时提供了更强大的类型支持。
# 01.模块基本概念
- 模块:一个独立的文件,包含代码和类型定义。
- 导出:将模块中的变量、函数、类等暴露给其他模块使用。
- 导入:从其他模块中引入导出的内容。
# 02.导出
# 2.1 导出变量、函数、类
使用 export 关键字导出内容。
// 导出变量
export const PI = 3.14;
// 导出函数
export function add(x: number, y: number): number {
return x + y;
}
// 导出类
export class Calculator {
static multiply(x: number, y: number): number {
return x * y;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.2 默认导出
每个模块可以有一个默认导出,使用 export default。
// logger.ts
export default function log(message: string): void {
console.log(message);
}
2
3
4
# 2.3 重新导出
使用 export ... from 语法重新导出其他模块的内容。
// utils.ts
export { add, PI } from "./math";
2
# 2.4 来看一个案例
两个函数的功能完全一样:
// 函数1 - 箭头函数 + const。没有显式声明返回类型,TypeScript自动推断为 void
export const showDefaultDebugBackground1 = (light?: UI.CustomCameraLight) => {
UI.TemplateUI.render({
background: 'black',
foreground: {
type: 'image',
imagePath: 'res/imgs/afs_bg_debug.png',
},
customCameraLight: light ?? 'green_lowlight'
})
};
// 函数2 - 传统函数声明
export function showDefaultDebugBackground2 (light?: UI.CustomCameraLight) : void {
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 03.导入
# 3.1 导入命名导出
使用 import { ... } from 语法导入命名导出。
// app.ts
import { PI, add, Calculator } from "./math";
console.log(PI); // 3.14
console.log(add(2, 3)); // 5
console.log(Calculator.multiply(2, 3)); // 6
2
3
4
5
6
# 3.2 导入默认导出
使用 import ... from 语法导入默认导出。
// app.ts
import log from "./logger";
// 导入外部模块
import {Logger} from "./utils/logger"; // 命名导入
import {JsSession} from "palm";
log("Hello, TypeScript!"); // "Hello, TypeScript!"
2
3
4
5
6
7
# 3.3 导入所有内容
使用 import * as 语法导入模块的所有内容。
// app.ts
import * as math from "./math";
console.log(math.PI); // 3.14
console.log(math.add(2, 3)); // 5
2
3
4
5
# 04.模块解析
TypeScript 支持两种模块解析策略:
- Classic:TypeScript 的旧版解析策略。
- Node:与 Node.js 的模块解析策略兼容(默认)。
# 4.1 相对路径导入
import { add } from "./math"; // 导入当前目录下的 math.ts
# 4.2 非相对路径导入
import { log } from "logger"; // 导入 node_modules 中的 logger 模块
这个ts文件,依赖了palm_constants.ts,后面可能依赖多个ts文件。我在编译时,如何打成一个js。
1.存放到dist目录下。 2.要求不要改变原ts文件。 3.不要创建合并ts文件。 4.建议给出直接编译的方案。
# 05.模块的编译
TypeScript 模块可以编译为不同的模块系统(如 CommonJS、AMD、ES Modules 等),通过 tsconfig.json 中的 module 选项配置。
{
"compilerOptions": {
"module": "CommonJS" // 编译为 CommonJS 模块
}
}
2
3
4
5
# 06.模块与命名空间
在大型项目中,不同模块可能使用相同的变量名或函数名。命名空间可以将这些代码隔离,避免冲突。
# 6.1 命名空间
命名空间是 TypeScript 早期用于组织代码的方式,现在推荐使用模块。
namespace MathUtils {
export function add(x: number, y: number): number {
return x + y;
}
}
console.log(MathUtils.add(2, 3)); // 5
2
3
4
5
6
7
# 6.2 避免命名冲突
namespace MathUtils {
export function add(a: number, b: number): number {
return a + b;
}
}
namespace StringUtils {
export function add(a: string, b: string): string {
return a + b;
}
}
console.log(MathUtils.add(1, 2)); // 3
console.log(StringUtils.add("Hello, ", "World")); // Hello, World
2
3
4
5
6
7
8
9
10
11
12
13
14
# 6.3 模块与命名空间结合
可以将命名空间放在模块中,但通常不推荐。
// math.ts
export namespace MathUtils {
export function add(x: number, y: number): number {
return x + y;
}
}
// app.ts
import { MathUtils } from "./math";
console.log(MathUtils.add(2, 3)); // 5
2
3
4
5
6
7
8
9
10
# 6.4 命名空间原理
在编译后的 JavaScript 中,命名空间会被转换为一个立即执行函数(IIFE),形成一个闭包,避免污染全局作用域。
// TypeScript
namespace MathUtils {
export function add(a: number, b: number): number {
return a + b;
}
}
// 编译后的 JavaScript
var MathUtils;
(function (MathUtils) {
function add(a, b) {
return a + b;
}
MathUtils.add = add;
})(MathUtils || (MathUtils = {}));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 6.5 模块VS命名空间
命名空间与模块的区别
| 特性 | 命名空间 | 模块 |
|---|---|---|
| 作用 | 组织代码,避免命名冲突 | 组织代码,支持模块化开发 |
| 文件作用域 | 默认全局,需手动隔离 | 默认隔离,需显式导出和导入 |
| 依赖管理 | 不支持自动依赖管理 | 支持自动依赖管理(如 import/export) |
| 适用场景 | 小型项目或旧代码迁移 | 大型项目或现代开发 |
# 6.6 最佳实践建议
1.避免过度使用
在现代 TypeScript 开发中,推荐使用模块(import/export)代替命名空间,因为模块更符合现代 JavaScript 的模块化标准。
2.与模块结合
如果需要兼容旧代码,可以将命名空间与模块结合使用。
// mathUtils.ts
export namespace MathUtils {
export function add(a: number, b: number): number {
return a + b;
}
}
// main.ts
import { MathUtils } from "./mathUtils";
console.log(MathUtils.add(1, 2)); // 3
2
3
4
5
6
7
8
9
10
# 07.动态导入
使用 import() 动态加载模块,返回一个 Promise。
async function loadModule() {
const math = await import("./math");
console.log(math.add(2, 3)); // 5
}
loadModule();
2
3
4
5
6
# 08.模块类型声明
# 8.1 声明模块
为第三方模块添加类型声明。
// my-module.d.ts
declare module "my-module" {
export function doSomething(): void;
}
2
3
4
# 8.2 导入类型
可以单独导入类型。
import type { Person } from "./types";
# 8.3 模块最佳实践
- 使用 ES 模块语法(
import/export)。 - 避免使用命名空间,优先使用模块。
- 将类型定义放在单独的文件中(如
types.ts)。 - 使用动态导入优化性能。