TypeScript 條件類型
本節(jié)介紹 TypeScript 高級類型中的條件類型(conditional type),條件類型就是在初始狀態(tài)并不直接確定具體類型,而是通過一定的類型運(yùn)算得到最終的變量類型。
1. 慕課解釋
條件類型用來表達(dá)非均勻類型映射,可以根據(jù)一個(gè)條件表達(dá)式來進(jìn)行類型檢測,從兩個(gè)類型中選出其中一個(gè):
T extends U ? X : Y
語義類似三目運(yùn)算符:若 T
是 U
的子類型,則類型為 X
,否則類型為 Y
。若無法確定 T
是否為 U
的子類型,則類型為 X | Y
。
2. 示例
declare function f<T extends boolean>(x: T): T extends true ? string : number
const x = f(Math.random() < 0.5) // const x: string | number
const y = f(true) // const y: string
const z = f(false) // const z: number
代碼解釋:
第 3 行,可以看到在條件不確定的情況下,得到了聯(lián)合類型 string | number
。
最后兩行,條件確定時(shí),得到了具體類型 string
或 number
。
3. 可分配條件類型
在條件類型 T extends U ? X : Y
中,當(dāng)泛型參數(shù) T
取值為 A | B | C
時(shí),這個(gè)條件類型就等價(jià)于 (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
,這就是可分配條件類型。
可分配條件類型(distributive conditional type)中被檢查的類型必須是裸類型參數(shù)(naked type parameter)。裸類型表示沒有被包裹(Wrapped) 的類型,(如:Array<T>
、[T]
、Promise<T>
等都不是裸類型),簡而言之裸類型就是未經(jīng)過任何其他類型修飾或包裝的類型。
4. 應(yīng)用場景
有了這些前置知識(shí),我們就可以分析一下 TypeScript 內(nèi)置的一些工具類型,就像在映射類型中介紹的可以通過 Partial<T>
,可以在項(xiàng)目中直接使用。
Exclude<T, U>
– 從T
中剔除可以賦值給U
的類型。Extract<T, U>
– 提取T
中可以賦值給U
的類型。NonNullable<T>
– 從T
中剔除 null 和 undefined。ReturnType<T>
– 獲取函數(shù)返回值類型。InstanceType<T>
– 獲取構(gòu)造函數(shù)類型的實(shí)例類型。
用第一個(gè)來舉例分析:
type T00 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'> // 'b' | 'd'
來看一下 Exclude<T, U>
的實(shí)現(xiàn)源碼:
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
再看一個(gè)進(jìn)階的例子,定義一種方法,可以取出接口類型中的函數(shù)類型:
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>
interface Part {
id: number
name: string
subparts: Part[]
firstFn: (brand: string) => void,
anotherFn: (channel: string) => string
}
type FnNames = FunctionPropertyNames<Part>
type FnProperties = FunctionProperties<Part>
代碼解釋:
倒數(shù)第二行,首先,遍歷整個(gè)接口,然后通過條件類型判斷接口的屬性值的類型是否是函數(shù)類型,如果是函數(shù)類型,取其屬性名。得到:
type FnNames = 'firstFn' | 'anotherFn'
倒數(shù)第一行,通過上一節(jié)介紹的工具函數(shù) Pick,拿到這個(gè)接口的所有函數(shù)類型成員集合:
type FnProperties = {
firstFn: (brand: string) => void
anotherFn: (channel: string) => string
}
5. 小結(jié)
學(xué)習(xí)到了本節(jié),可以深刻的體會(huì)到 TypeScript 中的類型不單單只是簡單的給變量進(jìn)行標(biāo)注,還可以通過各種運(yùn)算,得到很多有趣的結(jié)果。