條件類型
Flow 的條件類型允許您透過檢查輸入類型,在兩種不同的輸出類型之間有條件地選擇。這對於擷取類型的部分,或描述複雜的重載很有用。
基本用法
它的語法類似於條件運算式:CheckType extends ExtendsType ? TrueType : FalseType
。
如果 CheckType
是 ExtendsType
的子類型,則條件類型將評估為 TrueType
。否則,它將評估為 FalseType
。
以下範例說明了這兩種情況。
1class Animal {}2class Dog extends Animal {}3
4type TypeofAnimal = Dog extends Animal ? 'animal' : 'unknown'; // evaluates to 'animal'5type TypeofString = string extends Animal ? 'animal' : 'unknown'; // evaluates to 'unknown'
通用條件類型
這看起來可能不太有用,因為您已經知道它將評估為哪種類型。然而,結合泛型,您可以對類型執行複雜的運算。例如,您可以寫下類型級別的 typeof
運算子
1type TypeOf<T> =2 T extends null ? 'null' :3 T extends void ? 'undefined' :4 T extends string ? 'string' :5 T extends number ? 'number' :6 T extends boolean ? 'boolean' :7 T extends (...$ReadOnlyArray<empty>)=>mixed ? 'function' : 'object'8
9type T1 = TypeOf<null>; // evaluates to 'null'10type T2 = TypeOf<void>; // evaluates to 'undefined'11type T3 = TypeOf<string>; // evaluates to 'string'12type T4 = TypeOf<number>; // evaluates to 'number'13type T5 = TypeOf<boolean>; // evaluates to 'boolean'14type T6 = TypeOf<(string)=>boolean>; // evaluates to 'function'15type T7 = TypeOf<{foo: string}>; // evaluates to 'object'
函式回傳類型取決於輸入類型
條件類型也允許您直觀地描述選擇不同函式重載的條件
1declare function wrap<T>(value: T): T extends string ? { type: 'string', value: string }2 : T extends number ? { type: 'number', value: number }3 : { type: 'unsupported' }4
5const v1 = wrap(3); // has type { type: 'number', value: number }6const v2 = wrap('4'); // has type { type: 'string', value: string }7const v3 = wrap({}); // has type { type: 'unsupported' }
上述範例也可以使用函式重載來撰寫
1declare function wrap(value: string): { type: 'string', value: string }2declare function wrap(value: number): { type: 'number', value: number }3declare function wrap(value: mixed): { type: 'unsupported' }4
5const v1 = wrap(3); // has type { type: 'number', value: number }6const v2 = wrap('4'); // has type { type: 'string', value: string }7const v3 = wrap({}); // has type { type: 'unsupported' }
在條件類型中推論
您可以使用條件類型的功能來使用 infer
類型萃取類型的一部分。例如,內建的 ReturnType
是由條件類型提供支援的
1type ReturnType<T> = T extends (...args: $ReadOnlyArray<empty>) => infer Return ? Return : empty;2
3type N = ReturnType<(string) => number>; // evaluates to `number`4type S = ReturnType<(number) => string>; // evaluates to `string`
我們在此處使用推論類型來引入一個名為 Return
的新泛型類型變數,它可以在條件類型的類型分支中使用。推論類型只能出現在條件類型中 extends
子句的右側。Flow 將執行檢查類型和延伸類型之間的子類型檢查,以根據輸入類型 T
自動找出其類型。
在 type N = ReturnType<(string) => number>
的範例中,Flow 檢查 (string) => number
是否為 (...args: $ReadOnlyArray<empty>) => infer Return
的子類型,在此過程中,Return
被限制為 number
。
在執行上述範例的萃取時,您通常希望條件類型始終選擇成功萃取類型的真分支。例如,靜默選擇假分支並不好
1type ExtractReturnTypeNoValidation<T> =2 T extends (...args: $ReadOnlyArray<empty>) => infer Return ? Return : any;3
41 as ExtractReturnTypeNoValidation<string>; // no error :(
相反地,您可能希望 Flow 在輸入不是函式類型時產生錯誤。這可以透過新增約束到類型參數來達成
1type ReturnType<T: (...args: $ReadOnlyArray<empty>) => mixed> =2 T extends (...args: $ReadOnlyArray<empty>) => infer Return ? Return : any;3
41 as ReturnType<(string) => number>;51 as ReturnType<string>;
5:17-5:22: Cannot instantiate `ReturnType` because string [1] is incompatible with function type [2] in type argument `T`. [incompatible-type-arg]
分配條件類型
當泛型條件類型給定一個聯合類型作為類型引數時,條件會分配到聯合的成員。例如,上述的 TypeOf
範例可以分配到一個聯合
1type TypeOf<T> =2 T extends null ? 'null' :3 T extends void ? 'undefined' :4 T extends string ? 'string' : 'other';5
6type StringOrNull = TypeOf<string | null>; // evaluates to 'string' | 'null'
這會先分解聯合類型,然後將每個類型傳遞到條件類型以分別評估。在上述範例中,這看起來像
TypeOf<string | null>
--> (break up the union) --> TypeOf<string> | TypeOf<null>
--> (evaluate each conditional type separately) --> 'string' | 'null'
如果您想避免這種行為,您可以使用一元組類型包裝檢查類型和擴充類型
1type NonDistributiveTypeOf<T> =2 [T] extends [null] ? 'null' :3 [T] extends [void] ? 'undefined' :4 [T] extends [string] ? 'string' : 'other';5
6type Other = NonDistributiveTypeOf<string | null>; // evaluates to 'other'
此技巧之所以有效,是因為只有在檢查類型為泛型類型時,Flow 才會啟用條件類型的分配行為。上面的範例未選擇條件類型的任何 true 分支,因為 [string | null]
不是 [null]
、[void]
或 [string]
的子類型,因為元組是 不變 類型。
採用
若要使用條件類型,您需要升級您的基礎架構,以支援語法
flow
和flow-parser
:0.208.0。在 v0.208 至 v0.211.1 之間,您需要在 .flowconfig 中的[options]
標題下明確啟用它,新增conditional_type=true
。prettier
: 3babel
搭配babel-plugin-syntax-hermes-parser
。請參閱 我們的 Babel 指南 以取得設定說明。eslint
搭配hermes-eslint
。請參閱 我們的 ESLint 指南 以取得設定說明。