【问题标题】:Typescript packages that ship with .mjs and .d.ts, but without .d.mts - how to import with ESM enabled?附带 .mjs 和 .d.ts 但没有 .d.mts 的 Typescript 包 - 如何在启用 ESM 的情况下导入?
【发布时间】:2022-06-27 17:09:20
【问题描述】:

简介

借助 ECMAScript 模块支持 added in Typescript 4.7,在 TS 构建期间可能会涉及到几个新的文件扩展名,包括 .mjs.d.mts。如果项目启用了此功能,则 TS 编译器在进行模块解析(定位要导入的文件)时管理起来会更加复杂。使用新的 ESM 文件扩展名有两种简单的模块:

  1. 一个模块有.js实现,.d.ts声明文件
  2. 一个模块有.mjs实现,.d.mts声明文件

问题

并非所有软件包都符合上述类别。一些包附带.js.mjs 两种实现版本,但只有.d.ts 声明文件,没有 .d.mts

在这种情况下,解决规则是什么?似乎.mjs 优先于.js 但如果没有.d.mts 则拒绝工作,如果您不拥有导入的模块,这是有问题的。不修改包可以解决吗?


示例

对于通过以下配置启用 ESM 的项目

// package.json
"type": "module"

// tsconfig.json
"module": "Node16",
"moduleResolution": "node16"

这取决于带有 .js.mjs.d.ts 但没有 .d.mts 的软件包(例如 js-base64

$ ls -l node_modules/js-base64
base64.d.ts
base64.js
base64.mjs

然后当我尝试像这样导入它时

// myfile.ts
import { Base64 } from 'js-base64'

我收到一个错误:

找不到模块“js-base64”的声明文件。 '/myproj/node_modules/js-base64/base64.mjs' 隐含一个 'any' 类型

但是,如果我这样做了

$ ln -s node_modules/js-base64/base64.d.ts node_modules/js-base64/base64.d.mts

然后错误消失了,这表明我故意忽略了.d.ts

【问题讨论】:

  • 我尝试回答这个问题,我撰写了一个答案,但我犹豫要不要添加它。有几件事不对劲。首先,如果不在 ESM 模块中添加文件扩展名,您根本不应该能够解析导入,因此除非是拼写错误,否则 import { Base64 } from 'js-base64' 行没有多大意义。您应该必须为其添加文件扩展名。此外,除非您的项目配置为能够实现为两种不同的模块类型,否则您不应在同一个项目中有两个不同的文件扩展名。 (软链接文件 w/differnt ext 正在添加另一种类型)
  • 感谢您的浏览! 'js-base64' 是一个不属于我的项目的模块示例。这是一个从 npm 安装的包。据我了解,必须将扩展名指定为only for relative imports,作为我项目一部分的模块。同样,对于您的“不应该有 2 个不同的文件扩展名”点 - 因为那不是我的包我没有选择(除了对包的原始存储库做出某种更改),这就是包的方式已发货。
  • 哦,我明白了,你对导入是正确的。
  • 我刚刚安装了 js-base64 包,并在我正在处理的 ESM 模块中尝试了它,并且问题很容易重新创建。问题是,他没有包含 js-base64.d.mts 文件,他也有,因为他使用的是“*.mjs”文件类型。
  • 顺便说一句,您在解释问题方面做得很好。很容易重新创建。

标签: node.js typescript es6-modules


【解决方案1】:

在同一页面上吸引未来的读者

上面的问题询问了一个 package.json 文件,AToW (在撰写本文时) 是这样配置的。

js-base64/package.json:
{
  "main": "base64.js",
  "module": "base64.mjs",
  "types": "base64.d.ts",

  "files": [
    "base64.js",
    "base64.mjs",
    "base64.d.ts"
  ],

  "exports": {
    ".": {
      "import": "./base64.mjs",
      "require": "./base64.js"
    },
    "./package.json": "./package.json"
  },
}

问题:


当问题的作者将包文件作为 ESM 模块导入时,他会使用导入语句来尝试访问它,但是,他的 "TypeScript Compiler"aka em> TSC) 每次尝试使用导入的模块时都会抛出错误(如下所示)

Could not find a declaration file for module: "js-base64" '/myproj/node_modules/js-base64/base64.mjs' implicitly has an 'any' type


重申问题


模块能否在不重新配置包的情况下解析。为什么创建符号链接(如 Sergey 的问题所示)似乎可以解决正在发生的错误?



更详细的问题


这是一个双模块 Node 包,这意味着它包含两个构建,一个 CJS 构建和一个 ESM 构建。它被配置为仅在模块的入口点使用不同的文件,包的其余部分(理论上)应该是相同的。

为两个不同入口点配置的文件可以在我发布在此答案最顶部的package.json sn-p 中看到。

在我们开始之前,我想澄清一下,package.json 文件配置有一个问题,但是,在大多数情况下,它的配置是正确的。 ESM & CJS 入口点和构建都运行良好,唯一的问题是当 TypeScript 用户将模块作为 ECMAS-Module 导入时 - aka ESM — 它不解析 typed,因此,当问题作者尝试导入和使用包时,TypeScript 编译器会抛出类型错误。


当前配置的影响

不作为类型化模块解析有一些明显的含义。尽管它们对大多数人来说是显而易见的,但为了确保所有读者都在讨论中的同一点扎根,覆盖它们是件好事。


  1. 如果您使用纯 JavaScript,您可能不会注意到有任何问题。 JS 不使用类型,因此,将包导入纯 JavaScript 代码库应该可以做到——作为“ES-Module”或“Common-JS 模块” em> — 没有遇到任何问题。

  2. 那些使用 TypeScript 的人应该能够通过 import { ... } from 'js-base64' 和/或 require('js-base64') 语句解析模块,并且所有内容都应该被键入。

  3. 那些尝试使用 TypeScript 导入包的人(在 ESM 环境中)会遇到类型错误,他们应该注意到包没有使用类型解析。


为什么类型不能解析?它们不包含在base64.d.ts 文件中吗?

因此,包是用 TypeScript 编写的,通常双模块包将用 TypeScript 编写,或者至少使用一些转译器进行转译。在这种情况下,该包无疑是 TS 创作的包。我还没有彻底检查它的代码,但如果它曾经是纯 JS,并且在某个时候被转换为 TS,我不会感到惊讶。无论哪种方式,它都有一个正确发出的 TSC .d.ts 声明文件。


那为什么不解决呢?

由于它的配置方式,它无法解决。默认情况下,该包配置为解析为 “Common-JS 模块”。基本package.json 文件不包含“类型”字段,因此它默认为 CJS 模块。 package.json 文件的 "type" 字段可以设置为...

  1. "commonjs" (又名 CJS) 或者,
  2. "module" (又名 ESM)
如上所述,省略 "type" 字段会导致包解析为 CJS 模块。


为 ESM 配置模块,尤其是在尝试同时支持 CJS 时,已经成为一个非常复杂的考验,目前很多人不理解它涉及到 TypeScript 编写的包。这是因为对 ESM 的大部分支持都是新的,尽管 ESM 标准早在 2015 年就已包含在 ECMA-262 规范中。

需要注意的重要一点是 package-maintainer 使用 package.json 文件的“exports”字段为两种模块类型定义单独的入口点。

这是一段 JSON 代码:
  "types": "base64.d.ts",
  "exports": {
    ".": {
      "import": "./base64.mjs", // ESM entry point
      "require": "./base64.js" // CJS entry point
    },
    "./package.json": "./package.json"
  },

他本可以反过来做,但他没有,他按照你在上面看到的那样做。



解决方案是什么样的


问题可以在上面的sn-p中看到,types字段是他告诉TSC当包作为模块导入时声明文件在哪里,问题是,正如我上面指出的,包是尽管定义了 ESM 入口点,但默认解析为 CJS 模块。

要解决此问题,包需要具有与设置为 ESM 入口点的文件具有相同名称和文件扩展名的声明文件。解决问题的另一种方法或已经存在的base64.d.ts 声明文件需要显式配置以使用设置为 ESM 入口点的文件(即base64.mjs)进行解析。

我进去调整了一下,确认我现在写的是大炮。我在玩这个包时做的第一件事就是更改package.json 文件的"exports" 字段中的配置集。


##### 这就是“exports”字段的样子
// jD3V's adjusted package configuration

{
  "main": "base64.js",
  "module": "base64.mjs",
  "types": "base64.d.ts",

  "files": [
    "base64.js",
    "base64.mjs",
    "base64.d.ts"
  ],

  "exports": {
    ".": {
      "import": "./base64.mjs",
      "require": "./base64.js",
      "types": "./base64.d.ts"
    },
  },
}

在上面你可以看到包的配置使得 TypeScript 现在可以推断(从包装在“exports”字段中的“types”字段)base64.d.ts 文件应该解决所有导出问题。


他本可以这样写exports 字段:
以下是我从"TypeScript v4.7 Release Notes" 中获得的一点信息,sn-p 中的 cmets 非常有用。请尝试理解 cmets 想要表达的意思。
    "exports": {
        ".": {
            // Entry-point for `import "my-package"` in ESM
            "import": {


            // Where TypeScript will look.
            "types": "./base64.d.ts",


            // Where Node.js will look.
            "default": "./base64.mjs"
        },

        "require": "./base64.js",
    },
  }

JD3V package.json 文件和 TS v4.7 sn-p 之间的区别在于 "import" 字段(在 "exports" 字段中)将对象作为其分配值,因此;当types 位置在“TS v4.7”中设置时,它正在为“base64.mjs”设置一个特定文件,而在 JD3V sn-p 中它为所有导出设置一个特定的“类型”位置。

请注意:“就包装而言,使用此答案顶部显示的配置,此模块无法作为 ESM 模块导入 TypeScript 项目,而无需修复'base64 .d.ts' 文件已解析。因为它目前在导入 ESM 模块的包时无法解析。"


此外,这都是官方记录的,可以在下面的 2 个链接中找到:

TS v4.7: "ECMAScript Module Support in Node.js"

TS-Lang: package.json Exports, Imports, and Self-Referencing

【讨论】:

  • 这回答了问题的第一部分——“在这种情况下,解决规则是什么?”这对我来说很有价值,谢谢。既然你花时间帮助我,我认为这足以将赏金奖励给你。答案是从包作者的角度写的,但暗示我可以修改像js-base64这样的库的源代码。所以它没有回答“可以在不修改包的情况下解决这个问题吗?”从包装消费者的角度提出的问题的一部分。也许我应该把它提取到一个单独的问题中。
  • 这不仅仅是赏金,而是正确处理的问题。就像你自己一样,这对我也很重要。我认为随着时间的推移,这个主题将变得越来越重要(至少对于 Node.js/TypeScript 开发人员和 NPM Pkg 维护人员而言)
  • 谢尔盖我更新了我的答案。如果我正确理解您的要求,那么如果您仔细阅读,答案应该会对您有所帮助。我感谢你让我知道问题的一部分没有得到回答。整个问题都需要回答,这是一个很好的问题。我确实建议您添加一个更通用的标题,以便其他需要此信息的人可以在搜索中轻松找到它。
猜你喜欢
  • 1970-01-01
  • 2015-02-09
  • 2017-03-13
  • 2015-12-17
  • 2012-12-07
  • 1970-01-01
  • 2018-10-14
  • 2021-04-29
  • 2021-06-02
相关资源
最近更新 更多