简体   繁体   English

不能使用 (node)js 的“新”特性

[英]Can't use "new" features of (node)js

I stumbled upon the possibility to add a cause to the Error constructor in javascript.我偶然发现了向 javascript中的 Error 构造函数添加原因的可能性。

But when I try to use this feature my app does not start as it does not know this "new" constructor arg.但是当我尝试使用此功能时,我的应用程序无法启动,因为它不知道这个“新”构造函数 arg。

> 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 

All of the following commands stop with the above compile error以下所有命令都因上述编译错误而停止

nodemon
tsc && node dist/index.js
ts-node index.ts

I added the following script to my package.json (to be sure to ask the right instance of node and the other tools for its version)我将以下脚本添加到我的 package.json 中(确保询问正确的节点实例和其他工具的版本)

"check": "nodemon -v && node -v && tsc -v && ts-node -v && npm -v"

It returns它返回

2.0.19
v16.14.2
Version 4.7.4
v10.9.1
8.17.0

The feature should be available since node version 10.9.0该功能应该从节点版本 10.9.0 开始可用

@types/node@18.7.6

My tsconfig.json我的 tsconfig.json

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist/",
    "removeComments": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  }
}

I'm using VSC on Windows.我在 Windows 上使用 VSC。

Any idea what I need to change/update?知道我需要更改/更新什么吗?

Note: I'll ignore the nodemon and ts-node aspects of your question details because they are additional layers of complexity and not related to the core problem.注意:我将忽略您问题详细信息的nodemonts-node方面,因为它们是额外的复杂层,与核心问题无关。

Because you haven't shown your code, I'll provide a (contrived) example for this answer that both accesses the cause property of an error instance and uses it in construction to demonstrate the issue with your configuration and explain what's wrong and how to fix it:因为您没有显示您的代码,所以我将为此答案提供一个(人为的)示例,该示例既可以访问错误实例的cause属性,也可以在构造中使用它来演示您的配置问题并解释问题所在以及如何解决修理它:

Example code and initial configuration示例代码和初始配置

Note: you can manually copy all of the example files in this answer and save them on your storage device to follow along, or you can just copy this script and paste it into your browser's console to download the entire example project as a zip archive:注意:您可以手动复制此答案中的所有示例文件并将它们保存在您的存储设备上以进行后续操作,或者您可以复制此脚本并将其粘贴到浏览器的控制台中以将整个示例项目下载为 zip 存档:

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

Let's get started.让我们开始吧。 Here's an example TypeScript module:这是一个示例 TypeScript 模块:

./src/module.ts : ./src/module.ts

import {
  AssertionError,
  equal as assertEqual,
  ok as assert,
} from 'node:assert/strict';

/** Find the innermost nested cause */
function getRootException (exception: unknown): unknown {
  let root = exception;
  let current = exception;
  while (current instanceof Error) {
    current = current.cause;
    if (typeof current !== 'undefined') root = current;
  }
  return root;
}

function getQuotedReason (exception: unknown): string {
  const rootException = getRootException(exception);

  // Get a reason string, preferring an error message
  // if the value is an Error instance:
  const reasonStr = rootException instanceof Error
    ? rootException.message
    : String(rootException);

  // Return a quoted version of the string:
  return JSON.stringify(reasonStr);
}

function example () {
  try {
    throw new Error(`I don't have a cause`);
  }
  catch (ex) {
    console.log(`The final reason was: ${getQuotedReason(ex)}`);
  }

  try {
    const cause = new Error(`I'm the final error cause`);
    throw new Error(`It's not my fault!`, {cause});
  }
  catch (ex) {
    console.log(`The final reason was: ${getQuotedReason(ex)}`);
  }

  try {
    const nestedCause = new Error(
      `I'm the cause of the error that caused the top-level error`,
    );

    const cause = new Error(
      `I'm the cause of the top-level error`,
      {cause: nestedCause},
    );

    throw new Error(`I'm the top-level error`, {cause});
  }
  catch (ex) {
    console.log(`The final reason was: ${getQuotedReason(ex)}`);
  }

  try {
    assertEqual(true, false, `True isn't false`);
  }
  catch (ex) {
    assert(ex instanceof AssertionError, 'Expeted an AssertionError');
    assert(ex.message === `True isn't false`);
    console.log('Encountered the expected AssertionError');
  }
}

example();

Here, I'm using the TSConfig shown in your question, and I've just added the include top-level option because the module above (all of the TS source code in this example) is in the ./src directory:在这里,我使用的是您问题中显示的 TSConfig,并且我刚刚添加了include顶级选项,因为上面的模块(本示例中的所有 TS 源代码)位于./src目录中:

./tsconfig.json : ./tsconfig.json

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist/",
    "removeComments": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

And, for completeness, here's my npm package file:而且,为了完整起见,这是我的 npm package 文件:

./package.json : ./package.json

{
  "name": "so-73378375",
  "version": "0.1.0",
  "scripts": {
    "compile": "tsc",
    "example": "npm run compile && node dist/module.js"
  },
  "license": "MIT",
  "devDependencies": {
    "@types/node": "^18.7.6",
    "typescript": "^4.7.4"
  }
}

Also, note that I'm using the latest LTS version of Node at the time of this answer (it's slightly newer than yours, but still 16.x):另外,请注意,我在给出这个答案时使用的是最新的 LTS 版本的 Node(它比你的稍新,但仍然是 16.x):

% node --version
v16.17.0

If you're following along, now's a good time to npm install :如果您继续关注,现在是npm install的好时机:

% npm install

added 2 packages, and audited 3 packages in 1s

found 0 vulnerabilities

Determining the "cause" of your issue确定问题的“原因”

Let's start out by looking at your TSConfig: you've set compilerOptions.target to "es2017" .让我们从查看您的 TSConfig 开始:您已将compilerOptions.target设置为"es2017" I'm not sure what influenced this decision, but the TypeScript GitHub repo wiki provides Recommended Node TSConfig settings that you can use for guidance:我不确定是什么影响了这个决定,但 TypeScript GitHub 存储库 wiki 提供了推荐的节点 TSConfig 设置,您可以将其用于指导:

Node 16节点 16

 { "compilerOptions": { "lib": ["ES2021"], "module": "commonjs", "target": "ES2021" } }

There are also base configs for various environments here .这里还有各种环境的基本配置。

Some observations:一些观察:

The lib option is set in addition to target .除了target之外,还设置了lib选项。 The documentation for compilerOptions.lib in the TSConfig reference includes this information: TSConfig 参考中的compilerOptions.lib文档包括以下信息:

TypeScript also includes APIs for newer JS features matching the target you specify; TypeScript 还包括与您指定的target匹配的更新 JS 功能的 API; for example the definition for Map is available if target is ES6 or newer.例如,如果targetES6或更高版本,则Map的定义可用。

What this means is that (according to the way that TS resolves configuration right now) not setting lib in your config is the same as specifying an array with a single value equal to your target .这意味着(根据 TS 现在解析配置的方式)不在您的配置中设置lib与指定一个具有等于您的target的单个值的数组相同。 That means we can update your TSConfig's compilerOptions with these values and get the same behavior:这意味着我们可以使用这些值更新您的 TSConfig 的compilerOptions并获得相同的行为:

{
  "compilerOptions": {
    "lib": ["es2017"],
    "target": "es2017",
    // --- snip ---
  },
  // --- snip ---
}

We're not done making changes yet (we still need to change the actual lib and target values), but let's run the example script now to see what the compiler tells us:我们还没有完成更改(我们仍然需要更改实际的 lib 和目标值),但是现在让我们运行示例脚本来看看编译器告诉我们什么:

% npm run example

> so-73378375@0.1.0 example
> npm run compile && node dist/module.js


> so-73378375@0.1.0 compile
> tsc

src/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.

12     current = current.cause;
                         ~~~~~

src/module.ts:41:43 - error TS2554: Expected 0-1 arguments, but got 2.

41     throw new Error(`It's not my fault!`, {cause});
                                             ~~~~~~~

src/module.ts:54:7 - error TS2554: Expected 0-1 arguments, but got 2.

54       {cause: nestedCause},
         ~~~~~~~~~~~~~~~~~~~~

src/module.ts:57:48 - error TS2554: Expected 0-1 arguments, but got 2.

57     throw new Error(`I'm the top-level error`, {cause});
                                                  ~~~~~~~


Found 4 errors in the same file, starting at: src/module.ts:12

The last 3 compilation errors look very much like the one you described in your question.最后 3 个编译错误与您在问题中描述的非常相似。 Now that we've reproduced your issue, let's adjust the lib and target values to the one suggested by the TS wiki "ES2021" — note that the capitalization for this value doesn't matter (eg "ES2021" is the same as "es2021" ):现在我们已经重现了您的问题,让我们将libtarget值调整为 TS wiki "ES2021"建议的值 — 请注意,该值的大小写无关紧要(例如, "ES2021" "es2021" ):

{
  "compilerOptions": {
    "lib": ["es2021"],
    "target": "es2021",
    // --- snip ---
  },
  // --- snip ---
}

Now that we've changed those values, let's try again:现在我们已经改变了这些值,让我们再试一次:

% npm run example

> so-73378375@0.1.0 example
> npm run compile && node dist/module.js


> so-73378375@0.1.0 compile
> tsc

src/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.

12     current = current.cause;
                         ~~~~~

src/module.ts:41:43 - error TS2554: Expected 0-1 arguments, but got 2.

41     throw new Error(`It's not my fault!`, {cause});
                                             ~~~~~~~

src/module.ts:54:7 - error TS2554: Expected 0-1 arguments, but got 2.

54       {cause: nestedCause},
         ~~~~~~~~~~~~~~~~~~~~

src/module.ts:57:48 - error TS2554: Expected 0-1 arguments, but got 2.

57     throw new Error(`I'm the top-level error`, {cause});
                                                  ~~~~~~~


Found 4 errors in the same file, starting at: src/module.ts:12

Still the same 4 compilation errors, However: there's a useful suggestion in there after the first error (it was also there the first time we ran it):仍然是相同的 4 个编译错误,但是:在第一个错误之后有一个有用的建议(我们第一次运行它时也有):

Try changing the 'lib' compiler option to 'es2022' or later.

Arriving at the solution到达解决方案

If we examine the core TS library declaration files for ES features (in v 4.7.4 ), we find that the types related to the error cause property are located in lib.es2022.error.d.ts .如果我们检查 ES 特性的核心 TS 库声明文件(在 v 4.7.4中),我们会发现与错误cause属性相关的类型位于lib.es2022.error.d.ts中。 These types are used in lib.es2022.d.ts and later versions, but are actually not used in lib.es2021.d.ts .这些类型在lib.es2022.d.ts和更高版本中使用,但实际上在lib.es2021.d.ts中没有使用。

Therefore, we need to set the lib option to at least "es2022" .因此,我们需要将lib选项设置为至少"es2022"

Is the TS wiki recommendation outdated? TS wiki 推荐是否过时? Sort of (it's not exactly that simple).有点(这并不那么简单)。 Node is always evolving, and that recommendation was likely written for the first release of Node 16. However, support for the Error.cause property was added to Node in release version 16.9.0 on 2021-09-07 . Node 一直在发展,该建议可能是为 Node 16 的第一个版本编写的。但是,在 2021-09-07 的发布版本 16.9.0中,对Error.cause属性的支持已添加到 Node 中。

There's also another useful site that tracks Node.js ES feature support: https://node.green .还有另一个有用的站点跟踪 Node.js ES 功能支持: https://node.green

We only need to update the compilerOptions.lib value, and we can actually set it to anything later than "es2022" (eg "esnext" ), and TS will still use the compilerOptions.target value to appropriately downlevel incompatible language features.我们只需要更新compilerOptions.lib的值,我们实际上可以将其设置为晚于"es2022"的任何值(例如"esnext" ),并且 TS 仍将使用compilerOptions.target值来适当地降低不兼容的语言特性。 Here's a snippet from the documentation:这是文档中的一个片段:

The target setting changes which JS features are downleveled and which are left intact. target设置更改了哪些 JS 功能被降级,哪些保持不变。 For example, an arrow function () => this will be turned into an equivalent function expression if target is ES5 or lower.例如,箭头 function () => this如果target是 ES5 或更低,这将转换为等效的function表达式。

Changing target also changes the default value of lib .更改target也会更改lib的默认值。 You may “mix and match” target and lib settings as desired, but you could just set target for convenience [if newer language features aren't needed in your source code].您可以根据需要“混合和匹配” targetlib设置,但您可以为方便起见设置target [如果源代码中不需要更新的语言功能]。

That last bracketed clause is mine (not in the docs) and describes the exception of your case.最后一个括号子句是我的(不在文档中),并描述了您的案例的例外情况。

Let's actually update the lib value now:现在让我们实际更新lib值:

{
  "compilerOptions": {
    "lib": ["es2022"],
    // --- snip ---
  },
  // --- snip ---
}

and run the example one more time:并再次运行该示例:

% npm run example

> so-73378375@0.1.0 example
> npm run compile && node dist/module.js


> so-73378375@0.1.0 compile
> tsc

The final reason was: "I don't have a cause"
The final reason was: "I'm the final error cause"
The final reason was: "I'm the cause of the error that caused the top-level error"
Encountered the expected AssertionError

Compilation succeeds, so the program is run and we get the expected output — great!编译成功,程序运行,我们得到了预期的 output — 太棒了!

If you're reading this — thanks for following along.如果您正在阅读本文——感谢您的关注。 I hope that clarifies everything for you.我希望这能为你澄清一切。

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

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