esm 和 cjs 共用同一份类型 - 让 type 先 move to esm-only

太长了不想看?直接用 Rslib 就完事了

Move to esm-only (type)

在 node22 中支持了 require esm 之后,算是吹响了 dual 包(同时发 esm + cjs 产物) 到 esm-only 的最后号角,比如 antftu 的这篇文章 《Move to ESM-only》。但是大家似乎都没有关注到 .d.ts .d.mts .d.cjs,即类型文件关于 esm-only 的事情,本文是一些补充。

主要是解决这个报错:

The current file is a CommonJS module whose imports will produce 'require' calls

esm 和 cjs 共用同一份类型造成 FalseEsm 问题

.d.ts 文件 "使用 esm-only" 的历史比 .js 文件更早,在 github.com/egoist/tsup... 之前,大家已经不约而同的让 dual 包生成同一份类型,这实际上是一种错误用法,会造成 FalseEsm 和 FalseCjs 问题。

什么是 FalseEsmFalseCjs 问题?

当共用一份 .d.ts 时,根据 package.json 中是否含有 "type": "module" 有两种产物

1. 含有 "type": "module"

sh 复制代码
├── dist
│   ├── index.cjs
│   ├── index.d.ts
│   └── index.js
└── package.json
json 复制代码
// package.json
{
  "name": "my-package",
  "type": "module",
  "exports": {
    "types": "./dist/index.d.ts",
    "require": "./dist/index.cjs",
    "import": "./dist/index.js"
  }
}

由于设置了 "type": "module",需要 .js 和 .cjs 扩展名产物,对应到类型产物应该是 .d.ts 和 .d.cts。 .d.ts 会被认为是 esm,因此该包只发布了 esm 的 type ,在 are the types wrong? 会报以下错误

2. 不含有 "type": "module"

sh 复制代码
├── dist
│   ├── index.js
│   ├── index.d.ts
│   └── index.mjs
└── package.json
json 复制代码
// package.json
{
  "name": "my-package",
  "exports": {
    "types": "./dist/index.d.ts",
    "require": "./dist/index.js",
    "import": "./dist/index.mjs"
  }
}

FalseEsm 问题会更严重一些,因为会抛出一个这个错误

因为 ts 只拿到了 esm type,认为它是一个 pure esm 包,不能被 require 导入,所以无奈只能报错。

但其实这是个 dual 包,js 产物是对的且可正常运行,这个类型错误是一个噪声。

为 esm 和 cjs 都生成对应的 dts 文件

如何解决 FalseEsm 和 FalseCjs 问题?

github.com/egoist/tsup... 中,为 esm 和 cjs 都生成了 dts 文件

对应的产物为

sh 复制代码
├── dist
│   ├── index.d.cts <--- 
│   ├── index.cjs
│   │
│   ├── index.d.ts <---
│   └── index.js
└── package.json
json 复制代码
// package.json
{
  "name": "my-package",
  "type": "module",
  "exports": {
    "import": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "require": {
      "types": "./dist/index.d.cts",
      "default": "./dist/index.cjs"
    }
  }
}

忽略 .d.cts 的生成细节和正确性不谈,这样修改起码扩展名是对的。

还是回到 esm 和 cjs 共用同一份类型

随着 node 22 中支持了 require esm,ts 也有了对应更改,在 TS 5.8 中,无法 require esm 的这个错误在 module: "nodenext" 中不会再报

www.typescriptlang.org/docs/handbo...

这一改变,FalseEsm 问题的报错有了更多解决方案,

  • moduleResolution: bundler
  • 升级 ts 5.8 并设置 module: nodenext

这个问题后续只会影响到 "使用 tsc 编译到 commonjs" 并且 "需要支持 node <= 18" 的开发者,甚至大多数情况下只是噪声,而且使用 esm 的 type 为未来切到 pure esm 有利。

反而去生成 .d.cts 会困难许多。

总结

综上,在社区从 dual 包到 esm only 的大趋势里,esm 之前的诸多痛点得到了解决,包括 FalseEsm 类型问题。

我认为的两种最佳实践应是:

  • pure esm(.js + .d.ts, js 和 dts 产物均是 esm)
  • esm + cjs (.js + .cjs + .d.ts,js 产物是 dual,dts 产物是 esm)

而且这也是 Rslib 的两套默认模板

不需要再考虑 .d.cts 了,让 type 先 move to esm-only 吧。

参考

相关推荐
zhangminghuan12 小时前
微信小程序开发核心知识点全景解析(前端必备硬核基础)
前端·微信小程序·小程序
boooooooom12 小时前
尝尝咸淡:从朴素 RAG 到 Graph RAG——一个烹饪问答系统的三层检索升级之路
前端·后端·llm
cindershade12 小时前
让每条前端异常都能回答:该由谁修、为什么现在修
前端
bitbrowser13 小时前
Telegram提示尝试次数过多,换网络和重装有用吗
前端
研☆香13 小时前
前端简单的的变量声明
前端
赵大仁13 小时前
Next.js AI Route Handler 工程化:超时、流式与鉴权
前端·ai·鉴权·next.js·工程化
东风破_13 小时前
(拼多多考题)TypeScript 工具类型:Pick、Omit、Partial、Required
typescript
yuhaiqiang13 小时前
从这两件事就能看出 vibecoding 距离专业作品差距有多大?AI 能抹平技术,但抹不平品味 !
前端·后端·程序员
一次旅行14 小时前
DeepSeek‑V4‑Flash‑Vision‑Exp 小白入门实战|3种传图方式、完整可跑代码、避坑排障
java·前端·人工智能
其实防守也摸鱼14 小时前
Codex破局:前端组件秒级生成的技术文章大纲
开发语言·前端·人工智能·学习·安全·web安全