簡體   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