繁体   English   中英

如何使用 TypeScript 编译器 (TSC) 解决 Node.js ES6 (ESM) 模块。 TSC 不发出正确的文件扩展名

[英]How to resolve Node.js ES6 (ESM) Modules with the TypeScript Compiler (TSC). TSC doesn't emit the correct file-ext

我正在尝试将我的 TypeScript 项目转换为 JavaScript,但是,似乎有些不对劲。 我通过"module":"ES6"设置将项目配置为解析为 ES6 模块(又名 ESM),但它没有解决问题。


这是我的tsconfig.json配置的样子:
  {
    "compilerOptions": {
      "module": "es6",
      "target": "es6",
      "lib": ["es6"],
      "sourceMap": true,
    }
  }

使用一对模块的测试用例:

我使用两个模块编写了一个简单的测试用例 senario。

  1. 第一个模块module1.ts只导出一个常量,如下所示:

    •  export const testText = "It works;";
  2. 第二个模块main.ts只是从第一个模块导入导出:

    •  import { testText } from 'module1'; alert(testText);

第二个模块(或main.js )的 output 文件嵌入在我的index.html文件中,我在<script...>标签中添加了类型属性type="module" ,如下所示:

    <script src="main.js" type="module"></script>

当我使用Firefox (在 about:config 中设置了dom.moduleScripts.enabled )或Chrome Canary (设置了实验性 Web 平台标志)时,它不起作用。

Typescript 编译器似乎转译了 TS import { testText } from 'module1'; -statement 到 JS 语句import { testText } from 'module1'; . (注:两者完全一样)

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

是我做错了什么还是 Typescript "module": "es6"设置不能正常工作? 有没有办法以将 .js 文件扩展名添加到生成的导入语句的方式配置 tsc?

这是TypeScript 中一个令人困惑的设计选择

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

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

import { testText } from './module1.js';
alert(testText);

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

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

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

最初创作于 2022 年 12 月

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


ESM 并不是一项容易集成到 Node.js 中的技术。 当谈到用 TypeScript 语言编写 Node ES-Modules 时,实际上,在某个时间点,这看起来似乎永远不会发生。 IMO,似乎某些项目不想做他们现在已经做的事情,以支持节点后端 RTE 中的标准。 不管怎样,让我们​​继续...

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

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

到目前为止,该标志有效,或作为最佳解决方案(或直至 TS 4.7 Nightly Release) 应该注意的是,根据您正在工作的环境和项目,这仍然是目前最好的解决方案。


但是,已经提供了更好的解决方案。


在这个时间点发生了很大的变化:

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

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

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

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

这是 ESM tsconfig.*.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,
    }

撰写于:太平洋标准时间 2022 年 5 月 22 日上午 8:50

获得对modulemoduleResolution说明符的支持



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


TS 发布 版本 npm 命令执行 有支持吗?
TS 最新 v4.6 npm i -D typescript@latest
测试版 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",}


配置您的运行时环境

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


但为什么是jD3V? 为什么我需要配置我的 RTE? 这是怎么回事??? RTE现在不是一切吗?

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

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

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

我们需要告诉 node 我们将要执行一个 ECMAScript 模块,而不是一个 CommonJS 模块。 我们可以通过两种不同的方式之一来做到这一点。

  1. 首先是使用package.json配置文件的“type”字段来配置我们的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...
    }
  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();
基思答对了。

在您的main.ts中,而不是:

    import { testText } from 'module1';

尝试以下操作:

    import { testText } from 'module1.js';

在我的情况下,vscode intellisense 有效 + 我也得到了所需的输出文件main.js

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM