繁体   English   中英

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

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

我偶然发现了向 javascript中的 Error 构造函数添加原因的可能性。

但是当我尝试使用此功能时,我的应用程序无法启动,因为它不知道这个“新”构造函数 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 

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

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

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

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

它返回

2.0.19
v16.14.2
Version 4.7.4
v10.9.1
8.17.0

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

@types/node@18.7.6

我的 tsconfig.json

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

我在 Windows 上使用 VSC。

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

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

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

示例代码和初始配置

注意:您可以手动复制此答案中的所有示例文件并将它们保存在您的存储设备上以进行后续操作,或者您可以复制此脚本并将其粘贴到浏览器的控制台中以将整个示例项目下载为 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"); })();

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

./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();

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

./tsconfig.json

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

而且,为了完整起见,这是我的 npm package 文件:

./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"
  }
}

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

% node --version
v16.17.0

如果您继续关注,现在是npm install的好时机:

% npm install

added 2 packages, and audited 3 packages in 1s

found 0 vulnerabilities

确定问题的“原因”

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

节点 16

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

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

一些观察:

除了target之外,还设置了lib选项。 TSConfig 参考中的compilerOptions.lib文档包括以下信息:

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

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

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

我们还没有完成更改(我们仍然需要更改实际的 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

最后 3 个编译错误与您在问题中描述的非常相似。 现在我们已经重现了您的问题,让我们将libtarget值调整为 TS wiki "ES2021"建议的值 — 请注意,该值的大小写无关紧要(例如, "ES2021" "es2021" ):

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

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

% 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

仍然是相同的 4 个编译错误,但是:在第一个错误之后有一个有用的建议(我们第一次运行它时也有):

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

到达解决方案

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

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

TS wiki 推荐是否过时? 有点(这并不那么简单)。 Node 一直在发展,该建议可能是为 Node 16 的第一个版本编写的。但是,在 2021-09-07 的发布版本 16.9.0中,对Error.cause属性的支持已添加到 Node 中。

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

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

target设置更改了哪些 JS 功能被降级,哪些保持不变。 例如,箭头 function () => this如果target是 ES5 或更低,这将转换为等效的function表达式。

更改target也会更改lib的默认值。 您可以根据需要“混合和匹配” targetlib设置,但您可以为方便起见设置target [如果源代码中不需要更新的语言功能]。

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

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

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

并再次运行该示例:

% 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

编译成功,程序运行,我们得到了预期的 output — 太棒了!

如果您正在阅读本文——感谢您的关注。 我希望这能为你澄清一切。

暂无
暂无

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

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