cuicui.
返回全部文章
Web前端 共约 465 字 预计阅读 2 分钟

TS+ESM项目导入自定义模块不加扩展名就报错的问题

折腾了两个小时,我基本弄明白了如何在 TypeScript 中使用自定义的 ESM 模块。

一开始以为直接导入文件名就行,不需要扩展名。但是,这样使用 TSC 编译出来之后运行报错,因为 ESM 规范要求导入自定义模块必须带有扩展名。然而,TSC 编译出来的项目中导入模块是不带扩展名的,所以 Node.js 会报错。

于是,我想当然地写了 import router from "./router.ts";,认为 TypeScript 会自动处理扩展名。然而,并不是这样的。

TypeScript 直接不允许导入 .ts 后缀的文件,要求启用 allowImportingTsExtensions。但是加了这个选项之后,出现了更多的错误:

error TS2691: An import path cannot end with a '.ts' extension. Consider importing './router.js' instead.
error TS2835: Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './event.js'?

关于这个问题,在 GitHub 上有相关的讨论:https://github.com/microsoft/TypeScript/issues/16577

解决方案

  1. tsconfig.json 中将 moduleResolution 设置为 nodenext。设置之后,VS Code 导入模块时会自动加上 .js 后缀。
  2. 使用 import router from "./router.js";,注意这里扩展名为 .js

实际上,上面的报错也说明了问题:“An import path cannot end with a ‘.ts’ extension. Consider importing ’./router.js’ instead.”(导入路径不能以 .ts 结尾,考虑导入 ’./router.js’)

非常坑啊有没有,明明是 TypeScript,为什么要导入 JavaScript 呢?

ChatGPT 给出了解释:

您在 TypeScript 中导入模块时,使用的是相对路径或绝对路径来引用另一个文件。这与 JavaScript 中的导入语句类似。

当 TypeScript 编译器将 TypeScript 代码转换为 JavaScript 代码时,它会自动将 .ts 扩展名更改为 .js 扩展名,并生成一个新的 JavaScript 文件,其中包含编译后的 JavaScript 代码。

因此,即使您在 TypeScript 代码中导入的是 .ts 文件,最终在运行时实际上会加载并执行 .js 文件,因此需要在导入语句中指定 .js 扩展名。