假设在 ./utils/calcute.ts
中有一个工具函数 add()
export function add(a: number, b: number): number {
return a + b;
}
然后我们在 main.ts 中需要使用这个 add 函数
tsconfig 配置 module=esnext ,然后假设有如下 main.ts
文件
import { add } from "./utils/calcute";
add(1,2)
使用 tsc 编译后使用 node 运行编译后的 js 文件会报错
node ./dist/main.js
... 省略
code: 'ERR_UNSUPPORTED_DIR_IMPORT',
url: 'file:///home/xxxxxx/dist/utils/calcute'
原因是现在的 node 处理 esm 的 import 需要指定具体文件名(即类似 import ./utils/calcute.js
)。不写扩展名的 import 会报错
而 typescript 编译代码对 import 内 from "xxxx"
的部分是不会做任何处理直接保留的。按照 ts 官方的意思就是这部分是模块解析,不应该是 typescript 的工作而应交给 js 运行时(如 node 、浏览器)自己处理,所以 tsc 编译 ts 文件是会完整保留这部分不做任何变动的
基于这种方针,于是就有了两种解法
tsconfig 配置 module=nodenext 和 moduleResolution=nodenext ,然后 main.ts
内容如下
import { add } from "./utils/calcute.js"; // 需要添加 .js 扩展名
add(1,2)
说真的,当年我接触到这种写法的时候是大受震撼的。 在 ts 文件中写 import .js 实在过于丑陋了。我不解、我不适应、我无法接受
但这样的代码经过 tsc 编译后就能正常被 node 执行了,我也只能捏着鼻子用了
本来以为 esm 的问题也就这样了,但没想到到了 2025 年就乱套了
因为 bun, deno 的竞争,不思进取的 node 终于开始迭代起功能了。甚至还破天荒地添加了直接执行 typescript 代码的功能(运行的时候直接丢弃类型信息把 ts 当 js 跑)
这个功能现在在在新 node 中已经默认开启可用了,并且 typescript 也为了这个功能添加多个更新。所以可以预见今后用 node 直接执行 ts 会多起来
然后,这个功能在 esm 上就不出意外得出意外了。还是上面的代码 main.ts
内容如下:
import { add } from "./utils/calcute.js"; // 需要添加 .js 扩展名
add(1,2)
使用 node main.ts
执行后直接报错
node main.ts
... 省略
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///home/xxxxxxxx/utils/calcute.js'
嗯,因为模块的代码位于文件 utils/calcute.ts
中,而 import 语句中写的是 ./utils/calcute.js
,所以 node 理所当然的找不到对应的模块文件报错了
所以为了解决这个问题,tsconfig 后来添加了一个选项 allowImportingTsExtensions ,开启后在 main.ts
中需要将 import 改写成 import .ts 的形式
import { add } from "./utils/calcute.ts"; // 需要 import .ts ,而不是.js
add(1,2)
嗯,当年 typescript 的回旋镖就这么砸了回来,现在我们又必须在 ts 文件中写 import .ts 了。并且为了兼容这种写法 typesript 现在还不得不添加新的编译选项 allowImportingTsExtensions
来允许在 ts 文件中 import .ts
但是,这有个问题,启用这个选项必须也启用 noEmit ,也就是说在 typescript 官方那的说法是:我们没有被打脸啊,我们依旧不处理 import 的内容,你想 import .ts 可以,但是你这样写了的话就别用我们的 tsc 来把这种代码编译成 js 了
但问题是实际上开发中,使用 node 直接执行 ts 文件测试,然后在生产环境中使用 tsc 或其他工具编译成 js 运行会很常见
于是如果你想直接 node 执行 ts 代码,那就得放弃将使用 tsc 将代码编译为 js
目前这 esm import 写法已经乱成这样了,大家平时会怎么选?
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.