在同一页面上吸引未来的读者
上面的问题询问了一个 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 编译器会抛出类型错误。
当前配置的影响
不作为类型化模块解析有一些明显的含义。尽管它们对大多数人来说是显而易见的,但为了确保所有读者都在讨论中的同一点扎根,覆盖它们是件好事。
-
如果您使用纯 JavaScript,您可能不会注意到有任何问题。 JS 不使用类型,因此,将包导入纯 JavaScript 代码库应该可以做到——作为“ES-Module”或“Common-JS 模块” em> — 没有遇到任何问题。
-
那些使用 TypeScript 的人应该能够通过 import { ... } from 'js-base64' 和/或 require('js-base64') 语句解析模块,并且所有内容都应该被键入。
-
那些尝试使用 TypeScript 导入包的人(在 ESM 环境中)会遇到类型错误,他们应该注意到包没有使用类型解析。
为什么类型不能解析?它们不包含在base64.d.ts 文件中吗?
因此,包是用 TypeScript 编写的,通常双模块包将用 TypeScript 编写,或者至少使用一些转译器进行转译。在这种情况下,该包无疑是 TS 创作的包。我还没有彻底检查它的代码,但如果它曾经是纯 JS,并且在某个时候被转换为 TS,我不会感到惊讶。无论哪种方式,它都有一个正确发出的 TSC .d.ts 声明文件。
那为什么不解决呢?
由于它的配置方式,它无法解决。默认情况下,该包配置为解析为 “Common-JS 模块”。基本package.json 文件不包含“类型”字段,因此它默认为 CJS 模块。 package.json 文件的 "type" 字段可以设置为...
-
"commonjs" (又名 CJS) 或者,
-
"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 字段:
"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 个链接中找到: