2025 年 node 项目,乱成一锅粥的 typescript ESM import 写法该怎么选?

1 天前
 BeautifulSoap

假设在 ./utils/calcute.ts 中有一个工具函数 add()

export function add(a: number, b: number): number {
  return a + b;
}

然后我们在 main.ts 中需要使用这个 add 函数

写法 1, import 不带扩展名:

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 文件是会完整保留这部分不做任何变动的

基于这种方针,于是就有了两种解法

  1. 放弃 tsc 编译使用 bundle
  2. 下面的写法 2

写法 2:import .js

tsconfig 配置 module=nodenext 和 moduleResolution=nodenext ,然后 main.ts 内容如下

import { add } from "./utils/calcute.js"; // 需要添加 .js 扩展名

add(1,2)

说真的,当年我接触到这种写法的时候是大受震撼的。 在 ts 文件中写 import .js 实在过于丑陋了。我不解、我不适应、我无法接受

但这样的代码经过 tsc 编译后就能正常被 node 执行了,我也只能捏着鼻子用了

本来以为 esm 的问题也就这样了,但没想到到了 2025 年就乱套了

写法 3: import .ts

因为 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 写法已经乱成这样了,大家平时会怎么选?

2587 次点击
所在节点    Node.js
46 条回复
lqm
1 天前
用 tsx 执行
fds
1 天前
@rick13 #18 我拿来跑脚本还挺稳定的。
nomagick
1 天前
恕我直言 node.js 的 esm loader 写了十年还是半成品,基本算是做死了
就当 node.js 就只能运行纯 commonjs, tsc 的时候永远翻译成 cjs 。
如果想运行 esm 那就用其他运行时。
JamesMackerel
1 天前
所以那个 go 写的 tsc 还有没有消息……
shakaraka
1 天前
@rick13 #18 我都在 5 、6 个商业项目上用了 1 、2 年
opengg
1 天前
node 很多方面都是狗屎
craftsmanship
1 天前
@opengg 愿闻其详
uni
1 天前
5202 年了正确的方法是放弃 node 换 bun
Ketteiron
23 小时 22 分钟前
@nomagick #23 很多项目已经逐渐完全放弃 cjs ,也不提供 cjs 产物,全面转向 esm 是必然的事。
这跟 esm loader 没多大关系,主要是几万个 package 一开始不愿意支持 esm ,毕竟它还能跑对吧。
有些库作者激进地 esm-only ,用户又要问为什么不支持 cjs ,这十年是用户与作者们在拉扯,nodejs 对此是没什么办法的。
esbuild 之类的工具尽量解决历史遗留问题,nodejs 没必要重新实现一遍,因为未来某个时间点会放弃 cjs 。
Terry05
22 小时 51 分钟前
这东西都喷到前端身上,这跟前端有一点关系吗
molvqingtai
22 小时 9 分钟前
我的建议 tsc 检查类型,打包不要用 tsc
xu33
21 小时 14 分钟前
直接用 nextjs 有啥问题没,全 ts
musi
20 小时 52 分钟前
我选择用专业的工具进行打包,比如 esbuild/vite
zogwosh
20 小时 36 分钟前
nodejs 这种垃圾只配当一个纯粹的 js 运行时使用,
Zhousiru
19 小时 18 分钟前
可以尝试下 extensionless: https://www.npmjs.com/package/extensionless
pursuer
18 小时 49 分钟前
用 AMD ,想怎么解析你可以自己写,不适合工具链打包场景

https://github.com/partic2/partic2-iamdee

https://www.v2ex.com/t/1104713
mengshouer
18 小时 45 分钟前
现有项目有什么就用什么
nomagick
18 小时 26 分钟前
@Ketteiron 不你不懂,node.js 的 esm loader 指的是从硬盘网络或文本 buff 加载 js 代码数据并最终转化成 js 对象的过程,其中涉及静态和动态加载,esm 文件中使用 require, 被 require 的文件中使用 import ,被加载的可能是硬盘文件,url 或者代码文本 buff 。 在简单加载之外又涉及到多个切面的插件,专门的加载线程,以及 node.js native binding 的特殊处理。 整个过程比你想像得复杂得多,具体流程一锅粥,代码写得一团乱麻,内部推不动外部看不懂,功能没写完就发布,标记成 Beta/RC ,根本用不了。
FlashEcho
17 小时 30 分钟前
我感觉解法一是不是其实是最常见的,为啥你不用解法一?放弃 tsc 编译使用 bundle 不行吗
rocmax
15 小时 18 分钟前
前一阵整理了一下正在开发的 monorepo ,把/packages 下面所有内部模块都改为只用 tsc 输出 d.ts 不编译 js ,/apps 下全部用 bundler 打包。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1166656

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX