错误导致 TypeScript 无法识别属性

and*_*mel 24 javascript node.js typescript nodemon ts-node

我偶然发现可以在 javascript 中向 Error 构造函数添加原因

但是,当我尝试使用此功能时,我的应用程序无法启动,因为它不知道这个“新”构造函数参数。

> tsc && node dist/index.js
promo/promo-service/am-promo-request-handler.ts:43:104 - error TS2554: Expected 0-1 arguments, but got 2.        
43         throw new Error(`Can't read Maxmind GeoLite2 City db from mmdb file '${config.pathMmdbCity}'`, { cause: err});
Found 1 error in promo/promo-service/am-promo-request-handler.ts:43 
Run Code Online (Sandbox Code Playgroud)

以下所有命令均因上述编译错误而停止

nodemon
tsc && node dist/index.js
ts-node index.ts
Run Code Online (Sandbox Code Playgroud)

我将以下脚本添加到我的 package.json 中(确保询问正确的节点实例和其他工具的版本)

"check": "nodemon -v && node -v && tsc -v && ts-node -v && npm -v"
Run Code Online (Sandbox Code Playgroud)

它返回

2.0.19
v16.14.2
Version 4.7.4
v10.9.1
8.17.0
Run Code Online (Sandbox Code Playgroud)

该功能应该从节点版本 10.9.0 开始可用

@types/node@18.7.6
Run Code Online (Sandbox Code Playgroud)

我的 tsconfig.json

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist/",
    "removeComments": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  }
}
Run Code Online (Sandbox Code Playgroud)

我在 Windows 上使用 VSC。

知道我需要更改/更新什么吗?

jse*_*ksn 32

\n

注意:我将忽略您的问题详细信息的nodemonts-node方面,因为它们是复杂性的附加层,与核心问题无关。

\n
\n

因为您还没有显示您的代码,所以我将为此答案提供一个(人为的)示例,该示例既访问cause错误实例的属性,又在构造中使用它来演示您的配置问题并解释什么错误以及如何修复它:

\n

示例代码和初始配置

\n
\n

注意:您可以手动复制此答案中的所有示例文件,并将它们保存在存储设备上以供后续操作,或者您可以复制此脚本并将其粘贴到浏览器的控制台中,以将整个示例项目下载为zip 存档:

\n
(() => {\n  function createBase64DataUrl (mediaType, b64Str) {\n    return `data:${mediaType};base64,${b64Str}`;\n  }\n\n  function download (url, fileName) {\n    const a = document.createElement("a");\n    a.href = url;\n    a.download = fileName;\n    a.click();\n    a.remove();\n  }\n\n  const zipArchiveData = "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";\n  const dataUrl = createBase64DataUrl("application/zip", zipArchiveData);\n  download(dataUrl, "so-73378375.zip");\n})();\n
Run Code Online (Sandbox Code Playgroud)\n
\n

让我们开始吧。这是一个 TypeScript 模块示例:

\n

./src/module.ts

\n
import {\n  AssertionError,\n  equal as assertEqual,\n  ok as assert,\n} from \'node:assert/strict\';\n\n/** Find the innermost nested cause */\nfunction getRootException (exception: unknown): unknown {\n  let root = exception;\n  let current = exception;\n  while (current instanceof Error) {\n    current = current.cause;\n    if (typeof current !== \'undefined\') root = current;\n  }\n  return root;\n}\n\nfunction getQuotedReason (exception: unknown): string {\n  const rootException = getRootException(exception);\n\n  // Get a reason string, preferring an error message\n  // if the value is an Error instance:\n  const reasonStr = rootException instanceof Error\n    ? rootException.message\n    : String(rootException);\n\n  // Return a quoted version of the string:\n  return JSON.stringify(reasonStr);\n}\n\nfunction example () {\n  try {\n    throw new Error(`I don\'t have a cause`);\n  }\n  catch (ex) {\n    console.log(`The final reason was: ${getQuotedReason(ex)}`);\n  }\n\n  try {\n    const cause = new Error(`I\'m the final error cause`);\n    throw new Error(`It\'s not my fault!`, {cause});\n  }\n  catch (ex) {\n    console.log(`The final reason was: ${getQuotedReason(ex)}`);\n  }\n\n  try {\n    const nestedCause = new Error(\n      `I\'m the cause of the error that caused the top-level error`,\n    );\n\n    const cause = new Error(\n      `I\'m the cause of the top-level error`,\n      {cause: nestedCause},\n    );\n\n    throw new Error(`I\'m the top-level error`, {cause});\n  }\n  catch (ex) {\n    console.log(`The final reason was: ${getQuotedReason(ex)}`);\n  }\n\n  try {\n    assertEqual(true, false, `True isn\'t false`);\n  }\n  catch (ex) {\n    assert(ex instanceof AssertionError, \'Expeted an AssertionError\');\n    assert(ex.message === `True isn\'t false`);\n    console.log(\'Encountered the expected AssertionError\');\n  }\n}\n\nexample();\n\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,我正在使用您问题中显示的 TSConfig,并且我刚刚添加了include顶级选项,因为上面的模块(本示例中的所有 TS 源代码)都位于目录中./src

\n

./tsconfig.json

\n
{\n  "compilerOptions": {\n    "target": "es2017",\n    "module": "commonjs",\n    "sourceMap": true,\n    "outDir": "./dist/",\n    "removeComments": true,\n    "esModuleInterop": true,\n    "forceConsistentCasingInFileNames": true,\n    "strict": true,\n    "skipLibCheck": true\n  },\n  "include": ["src/**/*"]\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

为了完整起见,这是我的 npm 包文件:

\n

./package.json

\n
{\n  "name": "so-73378375",\n  "version": "0.1.0",\n  "scripts": {\n    "compile": "tsc",\n    "example": "npm run compile && node dist/module.js"\n  },\n  "license": "MIT",\n  "devDependencies": {\n    "@types/node": "^18.7.6",\n    "typescript": "^4.7.4"\n  }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

另外,请注意,在回答这个问题时,我正在使用最新的 LTS 版本的 Node(它比你的版本稍新,但仍然是 16.x):

\n
% node --version\nv16.17.0\n\n
Run Code Online (Sandbox Code Playgroud)\n

如果您正在跟进,那么现在是执行以下操作的好时机npm install

\n
% npm install\n\nadded 2 packages, and audited 3 packages in 1s\n\nfound 0 vulnerabilities\n\n
Run Code Online (Sandbox Code Playgroud)\n

确定问题的“原因”

\n

让我们首先查看您的 TSConfig:您已设置compilerOptions.target"es2017". 我不确定是什么影响了这个决定,但 TypeScript GitHub 存储库 wiki 提供了推荐的 Node TSConfig 设置,您可以将其用作指导:

\n
\n

节点16

\n
% node --version\nv16.17.0\n\n
Run Code Online (Sandbox Code Playgroud)\n
\n

这里还有各种环境的基本配置。

\n

一些观察结果:

\n

lib除了 之外还设置了该选项targetcompilerOptions.libTSConfig 参考中的文档包含以下信息:

\n
\n

TypeScript 还包括与您指定的较新 JS 功能相匹配的 API target;例如,如果是或更新,则 的定义Map可用。targetES6

\n
\n

这意味着(根据 TS 目前解析配置的方式)不在lib配置中进行设置与指定一个数组的单个值等于您的target. 这意味着我们可以使用这些值更新您的 TSConfigcompilerOptions并获得相同的行为:

\n
% npm install\n\nadded 2 packages, and audited 3 packages in 1s\n\nfound 0 vulnerabilities\n\n
Run Code Online (Sandbox Code Playgroud)\n

我们还没有完成更改(我们仍然需要更改实际的 lib 和目标值),但现在让我们运行示例脚本来看看编译器告诉我们什么:

\n
% npm run example\n\n> so-73378375@0.1.0 example\n> npm run compile && node dist/module.js\n\n\n> so-73378375@0.1.0 compile\n> tsc\n\nsrc/module.ts:12:23 - error TS2550: Property \'cause\' does not exist on type \'Error\'. Do you need to change your target library? Try changing the \'lib\' compiler option to \'es2022\' or later.\n\n12     current = current.cause;\n                         ~~~~~\n\nsrc/module.ts:41:43 - error TS2554: Expected 0-1 arguments, but got 2.\n\n41     throw new Error(`It\'s not my fault!`, {cause});\n                                             ~~~~~~~\n\nsrc/module.ts:54:7 - error TS2554: Expected 0-1 arguments, but got 2.\n\n54       {cause: nestedCause},\n         ~~~~~~~~~~~~~~~~~~~~\n\nsrc/module.ts:57:48 - error TS2554: Expected 0-1 arguments, but got 2.\n\n57     throw new Error(`I\'m the top-level error`, {cause});\n                                                  ~~~~~~~\n\n\nFound 4 errors in the same file, starting at: src/module.ts:12\n\n
Run Code Online (Sandbox Code Playgroud)\n

最后 3 个编译错误看起来非常像您在问题中描述的错误。现在我们已经重现了您的问题,让我们将libtarget值调整为 TS wiki \xe2\x80\x94 建议的值"ES2021",请注意,该值的大小写并不重要(例如"ES2021""es2021"):

\n
{\n  "compilerOptions": {\n    "lib": ["ES2021"],\n    "module": "commonjs",\n    "target": "ES2021"\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

现在我们已经更改了这些值,让我们再试一次:

\n
% npm run example\n\n> so-73378375@0.1.0 example\n> npm run compile && node dist/module.js\n\n\n> so-73378375@0.1.0 compile\n> tsc\n\nsrc/module.ts:12:23 - error TS2550: Property \'cause\' does not exist on type \'Error\'. Do you need to change your target library? Try changing the \'lib\' compiler option to \'es2022\' or later.\n\n12     current = current.cause;\n                         ~~~~~\n\nsrc/module.ts:41:43 - error TS2554: Expected 0-1 arguments, but got 2.\n\n41     throw new Error(`It\'s not my fault!`, {cause});\n                                             ~~~~~~~\n\nsrc/module.ts:54:7 - error TS2554: Expected 0-1 arguments, but got 2.\n\n54       {cause: nestedCause},\n         ~~~~~~~~~~~~~~~~~~~~\n\nsrc/module.ts:57:48 - error TS2554: Expected 0-1 arguments, but got 2.\n\n57     throw new Error(`I\'m the top-level error`, {cause});\n                                                  ~~~~~~~\n\n\nFound 4 errors in the same file, starting at: src/module.ts:12\n\n
Run Code Online (Sandbox Code Playgroud)\n

还是一样的4个编译错误!然而,在第一个错误之后,有一个有用的建议(我们第一次运行它时也有):

\n
Try changing the \'lib\' compiler option to \'es2022\' or later.\n
Run Code Online (Sandbox Code Playgroud)\n

得出解决方案

\n

如果我们检查ES 功能的核心 TS 库声明文件(在 v 中4.7.4),我们会发现与错误属性相关的类型cause位于lib.es2022.error.d.ts. 这些类型在及以后的版本中使用lib.es2022.d.ts,但实际上在lib.es2021.d.ts.

\n

因此,我们需要将该lib选项设置为至少"es2022".

\n

TS wiki 推荐已经过时了吗?有点(并不完全那么简单)。Node 总是在不断发展,该建议可能是为 Node 16 的第一个版本编写的。但是,在 2021 年 9 月 7 日发布的版本 16.9.0Error.cause中,对该属性的支持已添加到 Node 中。

\n

还有另一个有用的网站跟踪 Node.js ES 功能支持: https: //node.green

\n

我们只需要更新该compilerOptions.lib值,并且实际上可以将其设置为晚于"es2022"(例如"esnext")的任何值,并且 TS 仍然会使用该compilerOptions.target值来适当降低不兼容的语言功能。这是文档中的片段:

\n
\n

target设置会更改哪些 JS 功能被降级以及哪些功能保持不变。例如,如果ES5 或更低版本,箭头函数() => this将转换为等效表达式。functiontarget

\n

更改target还会更改 的默认值lib。您可以根据需要混合xe2x80x9c 和匹配 xe2x80x9dtarget和设置,但您可以只是为了方便而设置[如果源代码中不需要更新的语言功能]。libtarget

\n
\n

最后一个括号中的子句是我的(不在文档中),描述了您的情况的例外情况。

\n

lib现在让我们实际更新该值:

\n
{\n  "compilerOptions": {\n    "lib": ["es2017"],\n    "target": "es2017",\n    // --- snip ---\n  },\n  // --- snip ---\n}\n
Run Code Online (Sandbox Code Playgroud)\n

并再次运行该示例:

\n
% npm run example\n\n> so-73378375@0.1.0 example\n> npm run compile && node dist/module.js\n\n\n> so-73378375@0.1.0 compile\n> tsc\n\nThe final reason was: "I don\'t have a cause"\nThe final reason was: "I\'m the final error cause"\nThe final reason was: "I\'m the cause of the error that caused the top-level error"\nEncountered the expected AssertionError\n\n
Run Code Online (Sandbox Code Playgroud)\n

编译成功,因此程序运行,我们得到了预期的输出 \xe2\x80\x94\xc2\xa0great!

\n

如果您正在阅读本文 \xe2\x80\x94\xc2\xa0,感谢您的阅读。我希望这能为您澄清一切。

\n

  • 如果有什么需要 TL;DR.... (3认同)

cor*_*ard 21

简短回答:cause仅在 ES2022 及更高版本中受支持。compilerOptions您可以通过in告诉 TypeScript 通过以下两种方式接受它tsconfig.json

\n
选项 1:将您的target和/或更新libES2022
\n

如果您能够忽略对旧版浏览器的支持或对它们进行填充,则可以使用。

\n
{\n  "compilerOptions": {\n    "target": "es2022",\n    "lib": ["es2022"],\n  },\n}\n
Run Code Online (Sandbox Code Playgroud)\n
选项 2:仅添加相关es2022.error支持lib
\n

如果您还没有准备好实现总体目标,而只想包含受良好支持的特定功能,那么这是一个更好的选择。

\n
{\n  "compilerOptions": {\n    "target": "es2019",\n    "lib": ["es2019", "es2022.error"],\n  },\n}\n
Run Code Online (Sandbox Code Playgroud)\n

想要更多细节吗?请参阅@jsejcksn\xe2\x80\x99s非常彻底的答案

\n

  • 非常感谢您给出一个简短的答案而不是一本小说。 (3认同)