TypeScript 5.1 正式發(fā)布!你學(xué)到了什么?
6 月 1 日,TypeScript 5.1 正式發(fā)布。以下是該版本中新增的主要功能:
- 改進(jìn)函數(shù)返回值類型 undefined 的類型推斷
- getter和setter支持設(shè)置不同類型
- JSX 元素和 JSX 標(biāo)簽類型之間解耦類型檢查
- 帶命名空間的 JSX 標(biāo)簽
- typeRoots在模塊解析中被查詢
- JSX 標(biāo)簽支持鏈接光標(biāo)
- @Param JSDoc 標(biāo)簽中支持代碼補全
- 優(yōu)化
可以通過以下 npm 命令來安裝最新版本:
npm install -D typescript
改進(jìn)函數(shù)返回值類型 undefined 的類型推斷
在 JavaScript 中,如果函數(shù)沒有返回值,就會返回 undefined:
function foo() {
// 沒有 return
}
// x = undefined
let x = foo();
然而,在以前版本的 TypeScript 中,只有返回值類型為 void 和 any 的函數(shù)可以沒有 return語句。這意味著即使明確知道這個函數(shù)是返回 undefined 的,也需要至少有一個 return 語句。如下所示:
// ? 推斷 f1 返回 void
function f1() {
// no returns
}
// ? void 不需要 return 語句
function f2(): void {
// no returns
}
// ? any 不需要 return 語句
function f3(): any {
// no returns
}
// ? 聲明類型既不是 void 也不是 any 的函數(shù)必須返回一個值。
function f4(): undefined {
// no returns
}
有些時候,我們可能希望函數(shù)返回 undefined。在以前的版本中,可能需要顯式返回一個 undefined 或者顯式添加一個 return 語句。如下所示:
declare function takesFunction(f: () => undefined): undefined;
// ? 類型“() => void”的參數(shù)不能賦給類型“() => undefined”的參數(shù)。不能將類型“void”分配給類型“undefined”。
takesFunction(() => {
// no returns
});
// ? 其聲明類型不為 "void" 或 "any" 的函數(shù)必須返回值。
takesFunction((): undefined => {
// no returns
});
// ? 類型“() => void”的參數(shù)不能賦給類型“() => undefined”的參數(shù)。不能將類型“void”分配給類型“undefined”。
takesFunction(() => {
return;
});
// ? 顯式返回 undefined
takesFunction(() => {
return undefined;
});
// ? 顯式添加 return 語句
takesFunction((): undefined => {
return;
});
在 TypeScript 5.1 中,允許返回 undefined 的函數(shù)沒有 return 語句。如下所示:
// ?
function f4(): undefined {
// no returns
}
// ?
takesFunction((): undefined => {
// no returns
});
如果函數(shù)沒有 return 并且被傳遞給期望返回 undefined 的函數(shù)參數(shù),TypeScript 會推斷該函數(shù)的返回類型為 undefined。
// ?
takesFunction(function f() {
// ^ 返回值類型為 undefined
// no returns
});
// ?
takesFunction(function f() {
// ^ 返回值類型為 undefined
return;
});
為了解決另一個類似的痛點,在 TypeScript 的 --noImplicitReturns 選項下,僅返回 undefined 的函數(shù)現(xiàn)在具有與 void 類似的異常,因為并非每個代碼路徑都必須以顯式 return 結(jié)束。
// ? 啟用 '--noImplicitReturns'
function f(): undefined {
if (Math.random()) {
// ...
return;
}
}
getter和setter支持設(shè)置不同類型
TypeScript 4.3 使得 get 和 set 訪問器對可以指定兩種不同的類型成為可能。
interface Serializer {
set value(v: string | number | boolean);
get value(): string;
}
declare let box: Serializer;
// 允許賦值為 'boolean'
box.value = true;
// 類型為 'string'
console.log(box.value.toUpperCase());
最初要求 get 類型必須是 set 類型的子類型。以下寫法是有效的:
box.value = box.value;
但是,有許多現(xiàn)有的和提議的 API 在它們的 getter 和 setter 之間具有完全不相關(guān)的類型。例如,考慮一個最常見的例子——DOM 和 CSSStyleRule API 中的 style 屬性。每個樣式規(guī)則都有一個 style 屬性,即 CSSStyleDeclaration;但是,如果嘗試寫入該屬性,它只能使用字符串才有效。
TypeScript 5.1 現(xiàn)在允許 get 和 set 訪問器屬性設(shè)置不同的類型,前提是它們具有顯式類型注釋。雖然此版本的 TypeScript 尚未更改這些內(nèi)置接口的類型,但現(xiàn)在可以通過以下方式定義 CSSStyleRule:
interface CSSStyleRule {
get style(): CSSStyleDeclaration;
set style(newValue: string);
}
也允許以下方式使用:
class SafeBox {
#value: string | undefined;
// 只接受字符串
set value(newValue: string) {
}
// 必須檢查 undefined
get value(): string | undefined {
return this.#value;
}
}
這就類似于在 --exactOptionalProperties 下檢查可選屬性的方式。
JSX 元素和 JSX 標(biāo)簽類型之間解耦類型檢查
TypeScript 對 JSX 的一個痛點是它對每個 JSX 元素標(biāo)簽類型的要求。此版本的 TypeScript 使 JSX 庫可以更準(zhǔn)確地描述 JSX 組件可以返回的內(nèi)容,這具體意味著可以在 React 中使用異步服務(wù)端組件。
例如,有以下 JSX 元素:
// 自閉合的 JSX 標(biāo)簽
<Foo />
// 帶有開始/結(jié)束標(biāo)簽的常規(guī)元素
<Bar></Bar>
當(dāng)對 <Foo /> 或 <Bar></Bar> 進(jìn)行類型檢查時,TypeScript 總是會查找名為 JSX 的命名空間,并從中獲取一個名為 Element 的類型,也就是在查找 JSX.Element。
但是為了檢查 Foo 或 Bar 本身是否是有效的標(biāo)簽名稱,TypeScript 會粗略地獲取由 Foo 或 Bar 返回或構(gòu)造的類型,并檢查與 JSX.Element 的兼容性(或者如果類型是可構(gòu)造的,則檢查另一種稱為 JSX.ElementClass 的類型)。
這個限制意味著如果組件返回比 JSX.Element 更廣泛的類型,則無法使用組件。例如,JSX 庫可能可以接受一個返回字符串或Promise的組件。舉一個具體的例子,React 的未來版本提議對返回 Promise 的組件提供有限支持,但是現(xiàn)有版本的TypeScript 無法表達(dá)這種類型,除非徹底放寬 JSX.Element 的類型限制。
import * as React from "react";
async function Foo() {
return <div></div>;
}
let element = <Foo />;
// ~~~
// “Foo”不能用作 JSX 組件。其返回類型 "Promise<Element>" 不是有效的 JSX 元素。
為了向庫提供一種表達(dá)方式,TypeScript 5.1 現(xiàn)在會查找名為 JSX.ElementType 的類型。ElementType 精確指定什么可以有效用作 JSX 元素中的標(biāo)簽。因此,現(xiàn)在它的類型可能會被定義為類似于以下這樣:
namespace JSX {
export type ElementType =
// 所有有效的小寫標(biāo)簽
keyof IntrinsicAttributes
// 函數(shù)組件
(props: any) => Element
// 類組件
new (props: any) => ElementClass;
export interface IntrinsictAttributes extends /*...*/ {}
export type Element = /*...*/;
export type ClassElement = /*...*/;
}
帶命名空間的 JSX 標(biāo)簽
TypeScript 現(xiàn)在支持在使用 JSX 時使用帶命名空間的屬性名。
import * as React from "react";
// 這兩個是等價的:
const x = <Foo a:b="hello" />;
const y = <Foo a : b="hello" />;
interface FooProps {
"a:b": string;
}
function Foo(props: FooProps) {
return <div>{props["a:b"]}</div>;
}
當(dāng)名稱的第一部分是小寫字母時,在 JSX.IntrinsicAttributes 上以類似的方式查找命名空間標(biāo)簽名稱。
// 在某些庫的代碼中或在該庫的擴展中:
namespace JSX {
interface IntrinsicElements {
["a:b"]: { prop: string };
}
}
// 在我們的代碼中:
let x = <a:b prop="hello!" />;
typeRoots 在模塊解析中被查詢
當(dāng) TypeScript 指定的模塊查找策略無法解析路徑時,它現(xiàn)在將解析相對于指定 typeRoots 的包。
JSX 標(biāo)簽支持鏈接光標(biāo)
TypeScript 現(xiàn)在支持 JSX 標(biāo)簽名稱的鏈接編輯。也就是在編輯開始/結(jié)束標(biāo)簽時,會自動修改結(jié)束/開始標(biāo)簽。
這項新功能應(yīng)該適用于 TypeScript 和 JavaScript 文件,并且可以在 Visual Studio Code Insiders 中啟用。在 Visual Studio Code 中,勾選 Editor: Linked Editing 選項即可:
或者在 JSON 設(shè)置文件中配置 editor.linkedEditing:
{
// ...
"editor.linkedEditing": true,
}
Visual Studio 17.7 Preview 1 也將支持此功能。
@Param JSDoc 標(biāo)簽中支持代碼補全
在 TypeScript 和 JavaScript 文件中輸入 @param 標(biāo)簽時,TypeScript 現(xiàn)在提供代碼補全。
優(yōu)化
避免不必要的類型實例化
TypeScript 5.1 現(xiàn)在避免在已知不包含對外部類型參數(shù)的引用的對象類型中執(zhí)行類型實例化。這有可能減少許多不必要的計算,并將 material-ui 的文檔目錄的類型檢查時間減少 50% 以上。
聯(lián)合字面量類型檢查優(yōu)化
在檢查源類型是否屬于聯(lián)合類型時,TypeScript首先將使用該源類型的內(nèi)部類型標(biāo)識符進(jìn)行快速查找。如果查找失敗,則TypeScript會針對聯(lián)合類型中的每種類型檢查其兼容性。當(dāng)將字面類型與僅包含字面類型的聯(lián)合類型相關(guān)聯(lián)時,TypeScript現(xiàn)在可以避免針對聯(lián)合類型中的每種其他類型進(jìn)行完整遍歷。
這種優(yōu)化可以將此問題中的代碼的類型檢查時間從約 45 秒減少到約 0.4 秒。
進(jìn)行JSDoc解析時減少對掃描器的調(diào)用
當(dāng)舊版本的 TypeScript 分析 JSDoc 注釋時,它們會使用掃描器/分詞器將注釋分解為細(xì)粒度的標(biāo)記并將內(nèi)容拼湊在一起。這可能有助于規(guī)范化注釋文本,這樣多個空格就會合并為一個。但這也意味著解析器和掃描器會經(jīng)常來回跳轉(zhuǎn),從而增加 JSDoc 解析的開銷。
TypeScript 5.1 在將 JSDoc 注釋分解為掃描器/分詞器方面更改了邏輯。掃描器現(xiàn)在將更大的內(nèi)容塊直接返回給解析器,以便根據(jù)需要進(jìn)行處理。
這些更改已將幾個 10Mb 的主要是注釋的 JavaScript 文件的解析時間縮短了大約一半。
參考:https://devblogs.microsoft.com/typescript/announcing-typescript-5-1/