为何TypeScript允许在CommonJS模块中使用import语法?
环境配置
- Child.ts:
export const child = "Child";
- Parent.ts:
import { child } from "./Child"; console.log(child);
- tsconfig.json:
{ "compilerOptions": { "module": "node16", "esModuleInterop": true // 设为true或false无影响 } }
- package.json未设置
"type":"module"
问题
根据TypeScript的模块格式检测文档,原本预期:由于package.json未设置"type":"module",TypeScript会将Parent.ts和Child.ts视为CommonJS模块,应该对import语法报错并建议使用require(),但实际TypeScript允许在这类文件中使用import语法。想知道忽略了什么,是否是tsconfig的配置导致?
补充
另外,ESM模块要求相对路径带扩展名,照此TypeScript应该对import { child } from "./Child";报错,要求写成import { child } from "./Child.ts";,但实际也未报错。
解答
你混淆了TypeScript源文件的语法支持和最终编译输出的模块格式两个核心概念:
源文件语法的兼容性:TypeScript本身允许你在源文件中使用ESM的
import/export语法,无论最终编译目标是CommonJS还是ESM。当module设为node16且package.json无"type":"module"时,TypeScript会自动将你的ESM语法编译成CommonJS格式的代码(比如把import转换为require,export转换为module.exports相关逻辑),而非在编译阶段就报错禁止使用ESM语法。扩展名的处理逻辑:Node.js的ESM要求导入带扩展名是针对编译后的
.js文件的,但TypeScript在源文件解析阶段有自己的模块查找规则——它会自动尝试补充.ts、.d.ts、.tsx等扩展名来定位文件。当编译完成后,TypeScript会自动将导入路径替换为符合Node.js要求的格式(比如把./Child替换为./Child.js,对应编译后的CommonJS文件),因此源文件中不需要写.ts扩展名也不会报错。
简单来说,TypeScript作为转译层,会帮你处理源语法到目标模块格式的转换,以及扩展名的适配,这就是你看到和预期不符的核心原因。
内容的提问来源于stack exchange,提问作者H-H

