99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

TypeScript 5.8 新特性詳解

2025-03-25 14:14 更新

返回表達式分支的細致檢查

想象一下下面的代碼:

declare const untypedCache: Map<any, any>;
function getUrlObject(urlString: string): URL {
    return untypedCache.has(urlString) ?
        untypedCache.get(urlString) :
        urlString;
}

這段代碼的目的是從緩存中獲取 URL 對象,如果不存在就創(chuàng)建一個新的 URL 對象。但這里有個問題:我們忘記用輸入實際構造一個新的 URL 對象了。遺憾的是,TypeScript 通常無法捕捉到這種錯誤。

當 TypeScript 檢查像 cond ? trueBranch : falseBranch 這樣的條件表達式時,它的類型會被視為兩個分支類型的聯(lián)合。換句話說,它會獲取 trueBranchfalseBranch 的類型,然后將它們組合成一個聯(lián)合類型。在這個例子中,untypedCache.get(urlString) 的類型是 any,而 urlString 的類型是 string。問題就出在這里,因為 any 類型與其他類型交互時會“感染”其他類型。聯(lián)合類型 any | string 會被簡化為 any,所以當 TypeScript 開始檢查 return 語句中的表達式是否與預期的返回類型 URL 兼容時,類型系統(tǒng)已經丟失了能夠捕捉到此代碼中錯誤的信息。

在 TypeScript 5.8 中,類型系統(tǒng)對直接位于 return 語句內的條件表達式進行了特殊處理。條件的每個分支都會被檢查是否與包含函數(shù)的聲明返回類型(如果存在)兼容,因此類型系統(tǒng)可以捕捉到上面示例中的錯誤。

declare const untypedCache: Map<any, any>;
function getUrlObject(urlString: string): URL {
    return untypedCache.has(urlString) ?
        untypedCache.get(urlString) :
        urlString; // 錯誤!類型“string”不能分配給類型“URL”。
}

這一改動是作為更廣泛的未來改進的一部分在此拉取請求中完成的。


--module nodenext 下支持對 ECMAScript 模塊的 require()

多年來,Node.js 支持 ECMAScript 模塊(ESM)和 CommonJS 模塊共存。但兩者之間的互操作性存在一些挑戰(zhàn):

  • ESM 文件可以 import CommonJS 文件
  • CommonJS 文件 不能 require() ESM 文件

換句話說,從 ESM 文件中使用 CommonJS 文件是可行的,但反過來則不行。這給希望提供 ESM 支持的庫作者帶來了許多挑戰(zhàn)。這些庫作者要么不得不與 CommonJS 用戶打破兼容性,要么“雙重發(fā)布”他們的庫(為 ESM 和 CommonJS 提供單獨的入口點),要么無限期地停留在 CommonJS 上。雖然雙重發(fā)布聽起來像是一個不錯的折中方案,但它是一個復雜且容易出錯的過程,還會使包中的代碼量大致翻倍。

Node.js 22 放寬了一些限制,允許從 CommonJS 模塊中 require("esm") 調用 ECMAScript 模塊。Node.js 仍然不允許對包含頂級 await 的 ESM 文件進行 require(),但大多數(shù)其他 ESM 文件現(xiàn)在可以從 CommonJS 文件中使用。這為庫作者提供了一個重大機會,使他們可以在不雙重發(fā)布庫的情況下提供 ESM 支持。

TypeScript 5.8 在 --module nodenext 標志下支持這種行為。當啟用了 --module nodenext 時,TypeScript 將避免對這些對 ESM 文件的 require() 調用發(fā)出錯誤。

由于此功能可能會回退到 Node.js 的較早版本,目前沒有穩(wěn)定的 --module nodeXXXX 選項啟用此行為;然而,我們預計 TypeScript 的未來版本可以在 node20 下穩(wěn)定此功能。在此期間,我們鼓勵使用 Node.js 22 及更高版本的用戶使用 --module nodenext,而庫作者和使用較早 Node.js 版本的用戶應繼續(xù)使用 --module node16(或進行小更新到 --module node18)。

有關更多信息,請參閱我們對 require("esm") 的支持文檔。

--module node18

TypeScript 5.8 引入了一個穩(wěn)定的 --module node18 標志。對于那些堅持使用 Node.js 18 的用戶,此標志提供了一個穩(wěn)定的參考點,不包含 --module nodenext 中的某些行為。具體來說:

  • node18 下禁止對 ECMAScript 模塊的 require(),但在 nodenext 下允許
  • node18 下允許導入斷言(已棄用,改為導入屬性),但在 nodenext 下禁止

有關更多信息,請參閱 --module node18 拉取請求以及對 --module nodenext 的更改。


--erasableSyntaxOnly 選項

最近,Node.js 23.6 取消了對直接運行 TypeScript 文件的實驗性支持的標記;然而,只有在該模式下支持某些構造。Node.js 取消了對 --experimental-strip-types 的標記,這要求任何 TypeScript 特定的語法不能具有運行時語義。換句話說,必須能夠輕松地“擦除”或“剝離”文件中的任何 TypeScript 特定語法,留下一個有效的 JavaScript 文件。

這意味著像以下這樣的構造是不被支持的:

  • enum 聲明
  • 具有運行時代碼的 namespacemodule
  • 類中的參數(shù)屬性
  • 非 ECMAScript import =export = 賦值

這里有一些不工作的示例:

// ? 錯誤:一個 `import ... = require(...)` 別名
import foo = require("foo");

// ? 錯誤:一個具有運行時代碼的命名空間
namespace container {}

// ? 錯誤:一個 `import =` 別名
import Bar = container.Bar;

class Point {
    // ? 錯誤:參數(shù)屬性
    constructor(public x: number, public y: number) { }
}

// ? 錯誤:一個 `export =` 賦值
export = Point;

// ? 錯誤:一個枚舉聲明
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

像 ts-blank-space 或 Amaro(Node.js 中類型剝離的底層庫)這樣的類似工具也有相同的限制。如果這些工具遇到不符合要求的代碼,它們會提供有用的錯誤消息,但你仍然只有在實際嘗試運行代碼時才會發(fā)現(xiàn)代碼不工作。

這就是為什么 TypeScript 5.8 引入了 --erasableSyntaxOnly 標志。當啟用此標志時,TypeScript 會對具有運行時行為的大多數(shù) TypeScript 特定構造發(fā)出錯誤。

class C {
    constructor(public x: number) { }
    //          ~~~~~~~~~~~~~~~~
    // 錯誤!當啟用了 'erasableSyntaxOnly' 時,此語法不允許。
}

通常,你會想要將此標志與 --verbatimModuleSyntax 結合使用,這可以確保模塊包含適當?shù)膶胝Z法,并且不會發(fā)生導入省略。

有關更多信息,請參閱此處的實現(xiàn)。


--libReplacement 標志

在 TypeScript 4.5 中,我們引入了用自定義文件替換默認 lib 文件的可能性。這是基于從名為 @typescript/lib-* 的包中解析庫文件的可能性。例如,你可以通過以下 package.jsondom 庫鎖定到 @types/web 包的特定版本:

{
    "devDependencies": {
        "@typescript/lib-dom": "npm:@types/web@0.0.199"
    }
}

安裝后,應該存在一個名為 @typescript/lib-dom 的包,TypeScript 在 dom 被你的設置隱含時會一直查找它。

這是一個強大的功能,但也增加了一些額外的工作。即使你不使用此功能,TypeScript 也會一直進行此查找,并且必須監(jiān)視 node_modules 中的更改,以防一個 lib 替換包開始存在。

TypeScript 5.8 引入了 --libReplacement 標志,允許你禁用此行為。如果你不使用 --libReplacement,現(xiàn)在可以使用 --libReplacement false 禁用它。在未來,--libReplacement false 可能會成為默認值,因此如果你目前依賴此行為,應該考慮使用 --libReplacement true 明確啟用它。

有關更多信息,請參閱此處的更改。


在聲明文件中保留計算屬性名稱

為了使類中的計算屬性在聲明文件中具有更可預測的發(fā)出,TypeScript 5.8 將始終保留實體名稱(bareVariablesdotted.names.that.look.like.this)在類中的計算屬性名稱中。

例如,考慮以下代碼:

export let propName = "theAnswer";
export class MyClass {
    [propName] = 42;
    //  ~~~~~~~~~~
    // 錯誤!類屬性聲明中的計算屬性名稱必須具有簡單的字面量類型或 'unique symbol' 類型。
}

TypeScript 的早期版本在為此模塊生成聲明文件時會發(fā)出錯誤,并且會生成一個盡力而為的聲明文件,其中包含一個索引簽名。

export declare let propName: string;
export declare class MyClass {
    [x: string]: number;
}

在 TypeScript 5.8 中,示例代碼現(xiàn)在被允許,發(fā)出的聲明文件將與你編寫的代碼匹配:

export declare let propName: string;
export declare class MyClass {
    [propName]: number;
}

請注意,這不會在類上創(chuàng)建靜態(tài)命名的屬性。你仍然會得到一個實際上像 [x: string]: number 這樣的索引簽名,因此對于這種情況,你需要使用 unique symbol 或字面量類型。

請注意,編寫此代碼在 --isolatedDeclarations 標志下過去和現(xiàn)在都是錯誤;但我們預計,由于此更改,計算屬性名稱通常將被允許在聲明發(fā)出中。

請注意,使用 TypeScript 5.8 編譯的文件可能會生成一個在 TypeScript 5.7 或更早版本中不向后兼容的聲明文件。

有關更多信息,請參閱實現(xiàn)此功能的拉取請求。


在程序加載和更新上的優(yōu)化

TypeScript 5.8 引入了許多優(yōu)化,這些優(yōu)化可以同時改進構建程序的時間,以及在 --watch 模式或編輯器場景中基于文件更改更新程序的時間。

首先,TypeScript 現(xiàn)在避免了在規(guī)范化路徑時涉及的數(shù)組分配。通常,路徑規(guī)范化會將路徑的每個部分分割成一個字符串數(shù)組,根據相對段規(guī)范化路徑,然后使用規(guī)范分隔符將它們重新連接。對于包含許多文件的項目,這可能是一項重要且重復的工作。TypeScript 現(xiàn)在避免分配數(shù)組,而是更直接地在原始路徑的索引上操作。

此外,當進行不會改變項目基本結構的編輯時,TypeScript 現(xiàn)在避免重新驗證提供給它的選項(例如 tsconfig.json 的內容)。這意味著,例如,一個簡單的編輯可能不需要檢查項目的輸出路徑是否與輸入路徑沖突。相反,可以使用上次檢查的結果。這應該使大型項目中的編輯感覺更響應迅速。


值得注意的行為更改

這一部分突出了作為任何升級的一部分應該被承認和理解的一組值得注意的更改。有時它會突出棄用、刪除和新限制。它還可以包含功能改進的錯誤修復,但也可能通過引入新錯誤來影響現(xiàn)有構建。

lib.d.ts

DOM 生成的類型可能會對你的代碼庫的類型檢查產生影響。有關更多信息,請參閱與此版本的 TypeScript 相關的 DOM 和 lib.d.ts 更新的問題。


--module nodenext 下對導入斷言的限制

導入斷言是 ECMAScript 的提議添加,以確保導入的某些屬性(例如,“此模塊是 JSON,而不是可執(zhí)行的 JavaScript 代碼”)。它們被重新設想為一個名為導入屬性的提議。作為過渡的一部分,它們從使用 assert 關鍵字改為使用 with 關鍵字。

// 一個導入斷言 ? - 與大多數(shù)運行時不兼容。
import data from "./data.json" assert { type: "json" };

// 一個導入屬性 ? - 導入 JSON 文件的推薦方式。
import data from "./data.json" with { type: "json" };

Node.js 22 不再接受使用 assert 語法的導入斷言。因此,當在 TypeScript 5.8 中啟用 --module nodenext 時,如果遇到導入斷言,TypeScript 將發(fā)出錯誤。

import data from "./data.json" assert { type: "json" };
//                             ~~~~~~
// 錯誤!導入斷言已被導入屬性取代。請使用 'with' 而不是 'assert'。

有關更多信息,請參閱此處的更改。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號