網站搜尋

Typescript:如何解釋 extends 與函數類型之間的這種互動


範例 1 對我來說完全說得通

type X = (1 | 2 | 3) extends (infer I) ? [I] : never;

// X = [1 | 2 | 3]

例子 2 我不知道為什麼型別變數現在被交集了

type X = (
    ((_: 1) => void) | ((_: 2) => void) | ((_: 3) => void)
) extends ((_: infer I) => void) ? [I] : never;

// X = [1 & 2 & 3]

我的猜測是這與這個有某種關聯/相似之處:

type X = { x: number } | { y: number } | { z: number };
type Y = keyof X;

// Y = x & y & z

然而,我無法說服自己我從基本原理理解了它。很想聽聽這可以如何解釋。

答案#1

在釋出說明中解釋了從條件型別推斷型別的引入時機。它會是聯集還是交集取決於推斷型別的變異性。直觀地說,如果型別被推斷為多個值的共同型別,它可以是其中的任何一個(聯集),但如果它被推斷為多個函數的參數型別,它必須被所有函數接受(交集)。

摘自發行說明:

以下範例演示了在協變位置中,同一類型變數的多個候選者如何導致推斷出聯合類型:

type Foo<T> = T extends { a: infer U, b: infer U } ? U : never;
type T10 = Foo<{ a: string, b: string }>;  // string
type T11 = Foo<{ a: string, b: number }>;  // string | number

同樣地,對於反變位置中的同一類型變數有多個候選項會導致推斷出交叉類型:

type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>;  // string
type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>;  // string & number

關於此功能的更多討論可以在實現此功能的 PR 中找到。

順便一提,它可以實現一些很酷的技巧,例如將聯合類型轉換為交集類型。