TypeScript模块化开发指南:ES6与CommonJS对比及常见问题解决
本文深入解析TypeScript中的模块化机制,对比ES6标准模块与CommonJS的差异。重点讲解在Node.js环境中引入非ESM模块(如fs)时的类型错误及编译问题,提供解构导入、命名空间导入及配置esModuleInterop的三种解决方案,并简述命名空间的历史背景与现状。
前言
随着项目代码规模不断扩大,通过拆分模块进行组合管理成为提升后期维护效率的关键手段。
除了commonJS和es6之外,TypeScript同样原生支持模块化开发。
TypeScript中ES6模块化语法实现
自ES6(即ES2015)发布以来,TypeScript便遵循其模块化标准,采用导出(export)和导入(import)机制来管理模块。
- 具体写法如下:
导出模块:
myModule.ts
export const name: number = 123
export const getUser = (user: string): void => {
console.log(user)
}导入模块:
index.ts
import { name, getUser } from "./myModule";
getUser(name)- 值得注意的是,TypeScript的智能提示功能在此场景下表现优异。
若未显式引入变量,编辑器会通过波浪线提示建议的引入路径。

直接输入变量名并按下enter键,即可自动补全引入语句。


尽管智能提示功能强大,但仍需注意以下关键事项。
| 注意事项 | 详细 |
|---|---|
| 获取智能提示有条件 | 但是要注意的是: 这个提示只适用于export, 不适用于export default。因为export有固定的变量名,而export default导出没有名字和引入时随意起名字, TS不会去猜测 |
import引入的文件名 | import引入的文件名不能带上.ts, 因为会参与编译的过程, 编译后的结果为js,找不到ts这个文件 |
TypeScript中CommonJS模块化语法实现
若沿用传统写法,代码结构如下:
// 导出
module.exports = {
sum (a: number, b: numnber) {
return a + b
}
}
//引入
const mymodule = require('./xxx')此时观察到mymodule的类型变为any,导致丧失类型检查能力。
- 在
ts中采用特定写法,既能获得完整的类型检查,又能保持与前述写法一致的编译结果。
// 导出
exports = {
sum (a: number, b: numnber) {
return a + b
}
}
//引入
import mymodule = require('./xxx')可以看出两种写法存在明显差异。
因此,建议优先采用ES6标准,因其代表了后续的发展方向。
解决模块导入错误的方法
首先看一个示例,我们在代码中引入node中的fs模块。
TypeScript源文件内容:
import fs from "fs";
fs.readFileSync('./')编译后的JavaScript结果:
// tsconfig.json中设置module: CommonJs, 结果如下
exports.__esModule = true;
var fs_1 = require("fs");
fs_1["default"].readFileSync('./');此时会观察到一些潜在问题。
- 1. 在TypeScript源文件中书写时会报错。
- 2. 编译结果中,代码使用了default属性
.readFileSync。
由于编译后的fs对象不包含default属性,因此运行时报错。
报错原因分析
import fs from "fs";fs模块并非遵循标准ES Module导出规范,而是默认使用module.exports={}形式导出。直接import引入时,编译结果会附加default属性,而fs中并不存在该属性,从而导致错误。
解决方案
- 方案一:解构出具体方法使用,这样就不会依赖
default属性。
TypeScript源文件内容:
import { readFileSync } from "fs";
readFileSync('./')编译后的JavaScript结果:
exports.__esModule = true;
var fs_1 = require("fs");
(0, fs_1.readFileSync)('./');- 方案二:使用
* as fs进行全部导入,将module.exports内的所有成员导入。
TypeScript源文件内容:
import * as fs from "fs";
fs.readFileSync('./')编译后的JavaScript结果:
exports.__esModule = true;
var fs = require("fs");
fs.readFileSync('./');- 方案三:将
esModuleInterop配置为true,使TypeScript能够识别是否导出ES Module标准形式,并作出相应处理。
tsconfig.json
{
"compilerOptions": {
// 启用es模块化交互而非es模块导出
"esModuleInterop": true
}
}TypeScript源文件内容:
import fs from "fs";
fs.readFileSync('./')编译后的JavaScript结果:
// 生成辅助函数__importDefault
// 此时如果不是属于esmodule形式导出, ts会默认添加default属性,值设置为本身
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
var fs_1 = __importDefault(require("fs"));
fs_1["default"].readFileSync('./');补充:TypeScript命名空间
TypeScript拥有自己的模块格式,称为命名空间(namespaces),该标准早于ES模块标准。
这种语法在创建复杂的定义文件时具有诸多实用功能,且在DefinitelyTyped中仍被广泛使用。尽管该特性未被废弃,但命名空间中的大部分功能已存在于ES Modules中,官方建议采用后者以与JavaScript的发展方向保持一致。
总结
至此,关于如何在TypeScript中使用模块化及注意事项的介绍已完成。如需了解更多TypeScript模块化相关内容,请搜索本站历史文章或继续浏览后续文章,感谢大家的支持!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58








