接上文type challenge(easy部分)
關於分配條件型別,官方檔案描述地址。
之前看的時候沒真正理解,關於聯合型別的分配條件,官方檔案其實也沒有講得很明白,和翻譯無關,英文檔案一樣很模糊,這些天做type challenge
,發現有些題做出來的結果和預期不太一致,所以重新梳理這塊內容。
先說結論
聯合型別什麼時候會分配,必須符合4個條件(後面直接用條件1、條件2等代指下麵條件):
首先,只分配extends前的內容
- 無論這個extends是不是子斷言語句中的
- 例如
type Test<T> = 'b' extends 'b' ? (T extends 'b' ? true: false) : false;
, 其中的T extends 'b'
在子語句中,但事實上依舊是有效的
分配的內容未做任何處理
type Test<T> = keyof T extends null ? never: false;
,T
被keyof
運運算元處理了,因此不會分配- 官方檔案中,提到避免分配的方法
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
,能規避分配也是這個道理
- 分配內容必須作為引數傳入
- 傳入時是聯合型別
相關題目與解析
驗證條件1
type Test<T> = 'b' extends 'b' ? (T extends 'b' ? true: false) : false;
Test<'a'| 'b'> // boolean
可見在子條件中的extends
也符合自動分配,否則'a'|'b' extends 'b'
會返回false
,而不是true|false
驗證條件2
發現這個問題是在DeepReadonly,題目地址
這一題一看看過去,直接寫出如下:
type DeepReadonly<T> = keyof T extends never ? T : {readonly [k in keyof T]: DeepReadonly<T[k]>};
但是發現對於測試用例X2
不生效
type X2 = { a: string } | { b: number };
DeepReadonly<X2> // { a: string } | { b: number }
仔細看,雖然X2是聯合型別,但keyof T extends never
顯然不符合前面說的條件2,因此不會自動分配,而keyof ({ a: string } | { b: number })
值為never
。因此該題正確寫法如下:
type DeepReadonly<T> = {
readonly [P in keyof T]: keyof T[P] extends never ? T[P] : DeepReadonly<T[P]>;
};
驗證條件3
顯然,普通使用extands
不會觸發自動分配
type Test = 'a'|'b' extends 'a' ? true: false; // false
那麼,假設傳入的引數是聯合型別,extends
前的物件也是聯合型別呢?
type Test<T> = 'b' extends 'b' ? (keyof T extends 'b' ? true: false) : false;
type Result = Test<{a:1,b:string}|{a:2,b:number}> // false
這裡,引數T
是聯合型別,但extends
前進行了keyof
處理,但keyof {a:1,b:string}|{a:2,b:number}
結果為'a'|'b'
,依然是聯合型別,若這裡進行了自動分配,結果應是boolean
而非false
。
根據結果來看,這裡並未進行分配,這個例子同時違背了條件2和條件3
驗證條件4
type Test<T> = 'a'|'b' extends 'b' ? T: false;
Test<5> // false
條件4顯而易見,官方檔案上已經說的很明確了。
不注意優先順序導致的錯誤
在測試分配條件型別的規律時,曾因為一條用例卡了半天,用例如下:
type A = keyof null|undefined; // undefined
type UndefinedExtendsNull = undefined extends null ? true: false; //false
type Test<T> = keyof T extends null ? true: false;
Test<null|undefined>; // true !!!!
此時已經知道了,keyof T
會避免自動分配,因此對於Test<null|undefined>
,可以寫成
keyof null|undefined extends null ? true : false; // 這裡有個坑...
而keyof null|undefined
結果是undefined
,但是
type UndefinedExtendsNull = undefined extends null ? true: false; //false
結果是false
,同樣的式子,結果不一樣,一開始我以為是分配規律的理解有問題,但即使分配了,結果也應該是true|false
,也就是boolean
,而不是true
。
後來發現,type
是有優先順序的,且keyof
優先順序高於|
.
按理說keyof null|undefined
結果應該是never
,之所以會顯示結果是undefined
,是因為優先順序運算:
keyof null|undefined -> (keyof null)|undefined -> never|undefined -> undefined
在實際寫型別的時候,要重點注意優先順序問題