Typescript编译器忘记为ES6模块导入添加文件扩展名?

Cha*_*156 10 javascript typescript ecmascript-6 es6-modules

我正在尝试使用ES6模块解析将一个Typescript项目编译为JS,但似乎有些不对劲.

我的tsconfig.json看起来像这样:

{
    "compilerOptions": {
    "module": "es6",
    "target": "es6",
    "sourceMap": true,
    "lib": ["es6"]
  }
}
Run Code Online (Sandbox Code Playgroud)

我用两个模块构建了一个简单的测试用例.第一个模块(module1.ts)只导出一个常量:

export const testText = "It works!";
Run Code Online (Sandbox Code Playgroud)

第二个模块(main.ts)只是从第一个模块导入导出:

import { testText } from 'module1';
alert(testText);
Run Code Online (Sandbox Code Playgroud)

第二个模块(main.js)的输出文件包含在我的index.html中,带有type="module"-Attribute:

<script src="main.js" type="module"></script>
Run Code Online (Sandbox Code Playgroud)

当我使用Firefox(dom.moduleScripts.enabled在about:config中设置)和Chrome Canary(实验Web平台标志设置)进行测试时.在这两种情况下它都不起作用.

Typescript编译器似乎将TS import { testText } from 'module1';语句转换为JS语句import { testText } from 'module1';.(注意:两者完全一样)

正确的ES6 import语句将是:( import { testText } from 'module1.js'; 注意.js文件扩展名)如果我手动将文件扩展名添加到生成的代码中,它就可以工作.

我做错了什么或者Typescript "module": "es6"设置不正常吗?有没有办法以这样的方式配置tsc:.js文件扩展名被添加到生成的import语句中?

JΛY*_*ÐΞV 17

作者注释:“此答案的发布日期与最初创建答案的日期不同。由于 TypeScript 的更改,我不得不写两次新答案。因为日期在这篇文章中非常相关,我已将它们包含在几个地方,以防答案变得陈旧。这将帮助读者知道我何时引用了我在下面写的任何技术的某些版本。

最初创作于 2021 年 12 月

TS、ESM、NODE 堆栈的“es-module-specifier”标志修复


ESM 并不是一项容易融入 Node.js 的技术。当谈到用 TypeScript 语言编写 Node ES 模块时,在某个时间点,这实际上看起来永远不会发生。在我看来,某些项目似乎不想做他们现在已经做的事情来支持节点后端 RTE 中的标准。无论如何,让我们继续前进...

最初,当我回答这个问题时,我建议遇到此问题的人使用以下标志:

node --es-module-specifier-resolution=node
(仅供参考:解决方案来自片段所附的链接)

该标志起作用了,或者说是迄今为止最好的解决方案(或者直到 TS 4.7 Nightly Release 为止)应该注意的是,根据您正在工作的环境和项目,这仍然是目前最好的解决方案。


然而,更好的解决方案已经可用。


此时此刻发生了很多变化:

在 v4.6 的 Nightly 版本中,TypeScript 添加了对新模块和 moduleResolution 说明符的支持,这些说明符(“显然”)tsconfig.json. 它们是在v4.6每晚构建期间发布的,就在 v4.6 beta 发布之前不久,4.7 成为每晚构建。现在,v4.7是测试版,并且v4.8是新的夜间构建(我提到所有此版本的内容,只是为了确保您是最新的)。

这些是 TS 给我们的新说明符/设置
  1. "module": "NodeNext"
  2. "moduleResolution": "NodeNext"
已宣布 v4.7 将包含新设置

我这么说的原因是,新的 TSC 标志(又名 tsconfig 设置)在 中可用v4.6但是,如果您还记得的话,我在几段之前说过TypeScript决定不在当前最新版本( typescript@latest)版本中发布它们v4.6。因此,它们将正式包含在 TypeScript 中,v4.7不再处于测试版,并成为最新版本。

它们应该始终与"esModuleInterop": true, (除非“Node.js”方面发生变化。“esModuleInterop”设置简化了对 ESM 的支持,并且某些节点模块需要它才能与新的 ESM 导入一起正常工作/导出模块系统。

这是 ESMtsconfig.*.json配置模板,我将其添加为每个新项目的起点。

    "compilerOptions": {
        // Obviously you need to define your file-system
        "rootDir": "source",
        "outDir": "build", 

        // THE SETTINGS BELOW DEFINE FEATURE SUPPORT, MODULE SUPPORT, AND ES-SYNTAX SUPPORT
        "lib": ["ESNext"],
        "target": "ES2020",
        "module": "NodeNext",
        "moduleResolution": "NodeNext", 
        "esModuleInterop": true, // Eases ESM support
        "types": ["node"],
        "allowSyntheticDefaultImports": true,
    }
Run Code Online (Sandbox Code Playgroud)
撰写日期:2022 年 5 月 22 日上午 8:50(太平洋标准时间)

获得module&moduleResolution说明符的支持



因此,您可以使用几个不同的 TS 版本来支持新配置。您可以使用BetaNightly。下面的图表展示了 3 个可用版本。截至目前,typescript@latest是 @ v4.6,不支持新配置。


TS发布 版本 NPM 执行命令 有支持吗?
TS最新 v4.6 npm i -D typescript@latest
TS测试版 v4.7 npm i -D typescript@beta 是的
TS 每晚 v4.8 npm i -D typescript@next 是的

配置您的开发环境以使用最新版本

您需要配置环境才能使用新的 TypeScript 版本。我显然无法记录编辑器的配置,但VS Code似乎是TypeScript最常见的配置,而VS 2022使用类似的配置。

VS Code中,您将以下配置添加到工作区./.vscode/settings.json文件中。

  // "./.vscode/settings.json"
  { "typescript.tsdk": "./node_modules/typescript/lib",}

Run Code Online (Sandbox Code Playgroud)

配置您的运行时环境

(对于这个答案,它是 Node.js)


但为什么会这样呢?为什么我需要配置 RTE?这是怎么回事???RTE 不是什么都知道吗?

  • 不,RTE 不会自动知道一切。它必须有某种指示或线索。

**但是我们不是已经配置了tsconfig.json文件了吗?

  • 是的,但那是针对 TypeScript 的,现在我们需要确保节点已准备好进行 TS 配置。

我们需要告诉节点我们将执行 ECMAScript 模块,而不是 CommonJS 模块。我们可以通过两种不同的方式之一来完成此操作。

  1. 第一个是package.json使用配置文件的“类型”字段来配置我们的文件package.json
    // @file "package.json" 

    {
        "name": "project-name",
        "version: "2.4.16",
        "type": "module", // <-- Lets env know this project is an ESM

        // the rest of your package.json file configuration...
    }
Run Code Online (Sandbox Code Playgroud)
  1. 或者,您可以执行上面的一些操作,但是使用--input-type "module"标志

  2. 最后,您可以执行我喜欢执行的操作,即使用ESM文件扩展名创建每个文件。在 js 中是这样.ejs,在 typescript 中也是这样.ets

例如,您的索引文件将为index.ets,TSC 将发出index.ejs.

最后,重要的是您在编写“ES-Module”时了解导入的工作原理。我将列出一些注释,我认为这将是此信息的最佳格式。

  1. 导入文件时必须使用 JavaScript 文件扩展名。

  2. ESM 导入是使用URI导入的,而不是使用“possix 文件路径”导入。CJS 模块的情况正好相反。

  3. 特殊字符必须采用百分比编码,例如带有 %23 的 # 和 ? 与%3F。

  4. TypeScript 不会让您预先添加 MimeType,但 ESM 使用预先添加的数据类型,因此这是最佳实践。

例如,最佳实践是使用以下 URI 格式从 Node API 和库导入:

import EventEmitter from 'node:events';

const eEmit = new EventEmitter();
Run Code Online (Sandbox Code Playgroud)


Kei*_*ith 12

这是TypeScript 中的一个错误

在短期内,您可以通过指定输出文件来解决它:

main.ts指定.js扩展名和路径:

import { testText } from './module1.js';
alert(testText);
Run Code Online (Sandbox Code Playgroud)

这将module.ts正确拾取,但输出包含.js扩展名。

请注意,您还需要为本地文件添加前缀,./因为“裸”模块名称保留供将来使用。