類型推論

在 TypeScript 中,有幾個地方使用類型推論,在沒有明確類型註解時提供類型資訊。例如,在此程式碼中

ts
let x = 3;
let x: number
Try

x 變數的類型推論為 number。此類推論發生在初始化變數和成員、設定參數預設值,以及決定函式傳回類型時。

在多數情況下,類型推論都很直接。在以下各節中,我們將探討如何推論類型的一些細微差別。

最佳共用類型

當類型推論來自多個表達式時,這些表達式的類型會用於計算「最佳共用類型」。例如,

ts
let x = [0, 1, null];
let x: (number | null)[]
Try

要推論上方範例中 x 的類型,我們必須考慮每個陣列元素的類型。這裡我們針對陣列類型給出兩個選項:numbernull。最佳共用類型演算法會考慮每個候選類型,並選出與所有其他候選類型相容的類型。

由於最佳共用類型必須從提供的候選類型中選出,因此有些情況下,類型共用一個結構,但沒有任何一個類型是所有候選類型的超類型。例如

ts
let zoo = [new Rhino(), new Elephant(), new Snake()];
let zoo: (Rhino | Elephant | Snake)[]
Try

理想情況下,我們可能希望 zoo 推論為 Animal[],但由於陣列中沒有任何物件嚴格屬於 Animal 類型,因此我們不會對陣列元素類型進行任何推論。若要修正此問題,請在沒有任何一個類型是所有其他候選類型的超類型時,明確提供類型

ts
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
let zoo: Animal[]
Try

如果找不到最佳共用類型,則推論結果為聯合陣列類型,(Rhino | Elephant | Snake)[]

情境式輸入

在某些情況下,TypeScript 中的型別推論也適用於「反方向」。這稱為「內容型別」。當表達式的型別由其位置暗示時,就會發生內容型別。例如

ts
window.onmousedown = function (mouseEvent) {
console.log(mouseEvent.button);
console.log(mouseEvent.kangaroo);
Property 'kangaroo' does not exist on type 'MouseEvent'.2339Property 'kangaroo' does not exist on type 'MouseEvent'.
};
Try

在此,TypeScript 型別檢查器使用 `Window.onmousedown` 函式的型別來推論賦值右側函式表達式的型別。當它這樣做時,它能夠推論 `mouseEvent` 參數的 型別,其中包含 `button` 屬性,但不包含 `kangaroo` 屬性。

這是因為 `window` 已在其型別中宣告 `onmousedown`

ts
// Declares there is a global variable called 'window'
declare var window: Window & typeof globalThis;
// Which is declared as (simplified):
interface Window extends GlobalEventHandlers {
// ...
}
// Which defines a lot of known handler events
interface GlobalEventHandlers {
onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
// ...
}

TypeScript 也足夠聰明,可以在其他內容中推論型別

ts
window.onscroll = function (uiEvent) {
console.log(uiEvent.button);
Property 'button' does not exist on type 'Event'.2339Property 'button' does not exist on type 'Event'.
};
Try

基於上述函式被指定給 `Window.onscroll` 的事實,TypeScript 知道 `uiEvent` 是 UIEvent,而不是像前一個範例中的 MouseEvent。`UIEvent` 物件不包含 `button` 屬性,因此 TypeScript 會擲回錯誤。

如果此函式不在內容型別位置,則函式的引數將隱含具有 `any` 型別,且不會發出錯誤(除非您使用 noImplicitAny 選項)

ts
const handler = function (uiEvent) {
console.log(uiEvent.button); // <- OK
};
Try

我們也可以明確地提供型別資訊給函式的引數,以覆寫任何內容型別

ts
window.onscroll = function (uiEvent: any) {
console.log(uiEvent.button); // <- Now, no error is given
};
Try

但是,這段程式碼會記錄 `undefined`,因為 `uiEvent` 沒有稱為 `button` 的屬性。

內容型別適用於許多情況。常見情況包括函式呼叫的引數、賦值的右側、型別斷言、物件和陣列文字的成員,以及回傳陳述式。內容型別也會作為最佳共用型別中的候選型別。例如

ts
function createZoo(): Animal[] {
return [new Rhino(), new Elephant(), new Snake()];
}
Try

在此範例中,最佳共用類型有一組四個候選項:AnimalRhinoElephantSnake。其中,Animal 可由最佳共用類型演算法選取。

TypeScript 文件是一個開放原始碼專案。協助我們改善這些頁面,發送 Pull Request

此頁面的貢獻者
RCRyan Cavanaugh (51)
OTOrta Therox (17)
DRDaniel Rosenwasser (10)
MHMartin Hanzel (2)
TLATThink Like a Techy (1)
12+

最後更新:2024 年 3 月 21 日