背景
使用 ts-node 时,出现上述错误:
ts-node test.ts
import { isValid } from './validParentheses';
// 有效的括号
const s = "{[";
const res2 = isValid(s);
console.log('res', res2);
原因
模块导入方式和环境要求的导入方式不匹配。
具体场景要分 js 和 ts。
js:node
// b.js
export function fn() { console.log('b') }
// a.js
import { fn } from "./b.js";
fn();
执行:node a.js
,就会报上述错误。
因为 node 环境默认为 CommonJS,ESM 导入就会有这错误。
那怎么设置当前环境的模块化方式呢?
// package.json
{
"type": "module",
}
另外:b.js 的后缀不能省略。
当然也可以将 b.js
后缀改为 b.mjs
,但生产环境不建议这么使用。
ts:ts-node
Unknown file extension “.ts” for xxx.ts
如果是 ts 文件,还可能出现这个错误:Unknown file extension ".ts" for xxx.ts
什么时候出现这个错误?
当使用 ESM 导入,并且 type 设为 module 的时候。
这很奇怪啊?ESM 导入,然后 type 设为 module 那不是天经地义吗,欺负老实人?
- 原因在于 ts-node 执行的时候,它是先编译成 CommonJS 模块的 js 文件,然后用 node 执行 js 文件的。
对于 ts-node 来说 import 就像个标记符号,没有实际功能。
所以使用 ts-node,import 导入文件可以不写后缀。
本来 node 也是这么认为的,前面已经了解了 node 无法识别 import 会报:Cannot use import ...
但现在 type 设为 module,node 就能识别 ESM 了,import 语句就有实际功能了,但 node 无法 ts 文件啊。这就像执行 node ikun.mp4
一样,所以报了未知文件错误。
因此使用 ts-node ,package.json 不能将 type 设为 module,反倒应设为 commonjs,或者干脆没有 type 字段。
tsconfig.json
我的 test.ts 文件是 ESM 导入,也没有 type 字段,怎么还报Cannot use import ...
?
原因是一样的,依旧是环境不匹配。
对于 ts ,别忘了还有一个限制环境的文件:tsconfig.json。它的编译选项里面也有一个控制模块化的字段 module。
{
"compilerOptions": {
"module": "ESNext",
},
"include": ["src/"],
}
module 设为 ESM 后,ts-node 编译成的 js 文件是 esm 模块化标准。但 ts-node 里面的 node 执行时依旧是以 commonjs 标准执行的。这就回到了前文 js 章节的情况,自然会报错。
因此解决办法就是将 module 设为 CommonJS,或者也干脆删掉 module 字段。
有一种情况无法省略,当你使用了第三库的时候。
比如使用了 vitest,如果省略 module 字段,会对 ESM导入的 vitest 无法识别。这时显示设置为 CommonJS 即可。
解决办法
node & ts-node 执行 js & ts文件的模块化问题:
js
- package.json:
type
设为module
ts
- package.json:
type
设为commonjs
或省略 - tsconfig.json:
module
设为CommonJS
或省略,引入第三库时不能省略。
另外 ts-node 默认可以直接使用 esm 模块导入,所以简单使用时, package.json、tsconfig.json 这些文件反倒碍事,不要它们直接用就完了。