TypeScript中export =与import = require()语法含义及使用问题
报错直接原因
你代码里抛'CJSModule.ts' is not a module.ts(2306)的核心原因很简单:TS不会把仅手动写了module.exports = xxx、没有使用TS/ESM标准导出语法的文件识别为TS模块。你把文件里注释掉的export = helloCJS放开,删掉手动写的module.exports = helloCJS那行,报错会立即消失。
注意:export =本身就是TS提供的语法,当你编译目标是CommonJS时,TS会自动把这句语法编译成module.exports = helloCJS,完全不需要你手动写原生CJS的导出语句。
语法作用与使用场景
你对这套语法的适配方向有误解:它不是用来兼容你手动写的原生CJS导出语句的,而是TS为了覆盖CommonJS/AMD模块的导出逻辑提供的类型安全的模块语法,核心使用场景有两个:
- 当你写TS代码需要编译输出为CommonJS/AMD格式,且需要把整个模块导出为单个函数、类或值(而不是在exports上挂多个属性)时,用
export =导出,搭配import xxx = require()导入,TS可以完整推导类型,编译后的代码也完全符合CJS/AMD的模块规范。正确写法示例:
// CJSmodule.ts function helloCJS(): void { console.log("CJSModule"); } // 编译到CommonJS时自动生成 module.exports = helloCJS export = helloCJS;
// index.ts // TS会自动推导helloCJSModule1的类型为() => void,无报错 import helloCJSModule1 = require("./CJSModule"); helloCJSModule1();
- 给传统纯JS编写的CommonJS存量模块编写类型声明文件(
.d.ts)时,必须用这套语法才能准确匹配原模块的导出形状。比如一个老CJS包的导出是直接覆盖整个module.exports为一个函数,你写声明时就必须用export =对应,否则TS推导出的导入类型会和实际运行值不符。
为什么ES模块普及后还保留这套语法
核心原因是ES模块和CommonJS的导出逻辑存在不可调和的不兼容,ES标准的export default无法完全替代export =的能力:
- ES模块的默认导出编译到CommonJS时,只会在exports对象上挂载一个
default属性,也就是会编译成exports.default = xxx,不会直接覆盖整个module.exports对象。这和传统CJS模块直接替换整个exports对象的行为完全不一致。 - 举个最常见的场景:你维护一个需要兼容低版本Node.js(无原生ESM支持)的工具库,所有用户都是用
const lib = require('你的包')的方式引入。如果你用ES默认导出,用户引入后拿到的是{ default: 实际导出值 }的对象,必须写lib.default()才能调用,完全不符合CJS生态的使用习惯。这种场景下你只能用export =语法,才能编译出符合用户预期的CJS模块代码。 - 除此之外,npm上存在海量的CommonJS格式的存量包,这些包的类型定义必须依赖
export =语法才能准确描述,不可能因为ES标准语法的存在就抛弃整个存量生态。
内容的提问来源于stack exchange,提问作者JackyChen
相关产品推荐
相关产品推荐

