Node 14启用ES模块时,ts-node/esm无扩展名导入报错原因?
这是Node.js ESM模块系统、TypeScript编译逻辑,以及ts-node/esm loader三者交互时的典型问题,我来给你拆解清楚原因:
1. Node.js ESM的严格扩展名要求
Node.js的ECMAScript模块(ESM)和传统的CommonJS模块有个核心差异:ESM要求导入必须指定完整的文件扩展名。
CommonJS会自动尝试补全.js、.json、.node等扩展名,甚至会去查找同名目录下的index.js;但ESM严格遵循ES模块规范,规范里并没有自动补全扩展名的机制,所以Node.js在处理ESM导入时,会直接按照你写的路径去查找文件,找不到就抛出ERR_MODULE_NOT_FOUND。
2. TypeScript的编译行为与运行时加载的差异
当你用tsc编译TypeScript文件时,编译器会自动把无扩展名的导入(比如import { logger } from './logger')转换成带.js扩展名的导入(import { logger } from './logger.js')——因为编译后的产物是.js文件,这样Node.js运行编译后的代码时就能正确找到文件。
但你现在用的是ts-node/esm这个loader,它的作用是在运行时直接解析并执行TypeScript文件,而不是先编译成JS再运行。这时候TypeScript的编译逻辑没有提前介入,路径解析完全由Node.js的ESM加载器主导。
3. ts-node/esm的适配逻辑
ts-node/esm为了兼容Node.js ESM的扩展名要求,做了一个关键适配:它会把带.js扩展名的导入路径,映射到对应的.ts(或.tsx、.mts等)文件。
所以当你写import { logger } from './logger.js'时,Node.js的ESM加载器会先尝试找./logger.js,找不到的话,ts-node/esm会介入,去查找同名的.ts文件并加载它;但如果你写无扩展名的./logger,Node.js的ESM加载器不会尝试去匹配.ts文件,直接就报错了。
简单总结:Node.js ESM强制要扩展名,ts-node/esm通过识别.js扩展名来关联对应的ts文件,无扩展名的话Node.js根本不会去搜索ts文件,自然就找不到资源了。
内容的提问来源于stack exchange,提问作者dagda1

