類型守衛
Flow 讓您可以定義其回傳表達式對參數 param
編碼一些類型謂詞的函式。此謂詞會註解在回傳類型註解的位置,為 param is PredicateType
。它宣告如果函式回傳 true
,則 param
的類型為 PredicateType
。
此類函式的語法為
function predicate(param: InputType): param is PredicateType {
return <some_expression>;
}
此函式的類型也可以用類型守衛註解來撰寫
type PredicateFunc = (param: InputType) => param is PredicateType;
基本用法
我們來看一個簡單的範例,其中我們定義一個類型守衛函式,然後使用它來精簡一些值。
定義類型守衛函式
1type A = { type: "A"; data: string };2type B = { type: "B"; data: number };3type AorB = A | B;4
5function isA(value: AorB): value is A {6 return value.type === "A";7}
我們定義了一個資料類型 AorB
,它是兩個類型 A
和 B
的不相交聯集,每個類型都有用作標籤的屬性 type
。
我們也寫了一個針對類型 AorB
物件定義的 *使用者定義類型守衛* 函式 isA
。當其輸入的 type
屬性值為 "A"
時,此函式會回傳 true
。使用 A
和 B
的定義,Flow 可以證明當 type
的值為 "A"
時,value
的類型將會是 A
。
使用類型防護函數精煉值
已宣告類型防護的函數可用於在條件式中精煉值。在上面的範例中,我們可以使用 isA
將類型為 AorB
的變數精煉為僅 A
1type A = { type: "A"; data: string };2type B = { type: "B"; data: number };3type AorB = A | B;4
5function isA(value: AorB): value is A {6 return value.type === "A";7}8
9function test(x: AorB) {10 if (isA(x)) {11 // `x` has now been refined to type A.12 // We can assign it variables of type A ...13 const y: A = x;14 // ...and access A's properties through `x`15 const stringData: string = x.data;16
17 // As a sanity check, the following assignment to B will error18 const error: B = x; 19 }20}
18:22-18:22: Cannot assign `x` to `error` because string [1] is incompatible with number [2] in property `data`. [incompatible-type]18:22-18:22: Cannot assign `x` to `error` because string literal `A` [1] is incompatible with string literal `B` [2] in property `type`. [incompatible-type]
在條件式的 then 分支 if (isA(x))
中,x
將具有類型 A
。
使用 Array.filter
精煉
Flow 會辨識您在類型為 Array<T>
的陣列上呼叫 filter
,並帶有包含類型防護的 callback 函數,類型為 (value: T) => value is S
。它會使用這個產生類型為 Array<S>
的輸出陣列。請注意,S
必須是陣列元素 T
類型的子類型。
例如
1type Success = $ReadOnly<{type: 'success', value: 23}>;2type Error = $ReadOnly<{type: 'error', error: string}>;3
4type Response =5 | Success6 | Error7
8function filterSuccess(response: Array<Response>): Array<Success> {9 return response.filter(10 (response): response is Success => response.type === 'success'11 );12}13
14function filterError1(response: Array<Response>): Array<Error> {15 const result = response.filter(16 (response): response is Success => response.type === 'success'17 );18 // The following is expected to error19 return result; 20}21
22function filterError2(response: Array<Response>): Array<Error> {23 const result = response.filter(24 // The following is expected to error25 (response): response is Error => response.type === 'success' 26 );27 return result;28}
19:10-19:15: Cannot return `result` because property `error` is missing in object type [1] but exists in object type [2] in array element. [prop-missing]19:10-19:15: Cannot return `result` because property `value` is missing in object type [1] but exists in object type [2] in array element. [prop-missing]19:10-19:15: Cannot return `result` because string literal `success` [1] is incompatible with string literal `error` [2] in property `type` of array element. [incompatible-return]25:38-25:64: Cannot return `response.type === 'success'` because property `error` is missing in object type [1] but exists in object type [2] in the type inferred for type guard parameter `response` [3]. [prop-missing]25:38-25:64: Cannot return `response.type === 'success'` because property `value` is missing in object type [1] but exists in object type [2] in the type inferred for type guard parameter `response` [3]. [prop-missing]25:38-25:64: Cannot return `response.type === 'success'` because string literal `success` [1] is incompatible with string literal `error` [2] in property `type` of the type inferred for type guard parameter `response` [3]. [incompatible-return]
在 filterError1
中,過濾會產生與預期回傳類型 Array<Error>
不相容的 Array<Success>
。
在 filterError2
中,謂詞 response.type === 'success'
用於將 Response
精煉為 Success
,而非 Error
。
定義類型防護函數
為了確保使用類型防護函數的精煉是健全的,Flow 會執行與這些函數相關的數個檢查。
謂詞參數是函數的常規參數
在形式為 parameter is Type
的類型防護註解中,parameter
需要屬於目前函數的參數清單。
1function missing(param: mixed): prop is number { 2 return typeof param === "number";3}
1:33-1:36: Cannot find type guard parameter `prop` [1] in the parameters of this function (type). [function-predicate]
它不能是解構模式中綁定的參數,或 rest 參數
1function destructuring({prop}: {prop: mixed}): prop is number { 2 return typeof prop === "number";3}
1:48-1:51: A type guard parameter `prop` [1] cannot reference pattern parameter `prop` [2]. [function-predicate]
1function rest(...value: Array<mixed>): value is Array<mixed> { 2 return Array.isArray(value);3}
1:40-1:44: A type guard parameter `value` [1] cannot reference rest parameter `value` [2]. [function-predicate]
謂詞類型與參數類型一致
類型防護 Type
需要與參數類型相容。換句話說,給定定義
function isT(x: ParamType): x is Type {
return ...
}
Flow 會檢查 Type
是否為 ParamType
的子類型。因此,以下會是一個錯誤
1function isNumber(x: string): x is number { 2 return typeof x === "number";3}
1:36-1:41: Cannot use number [1] as type prediate for parameter `x` because number [1] is incompatible with string [2]. A user defined type guard needs to be compatible with its parameter's type. [incompatible-type-guard]
類型防護函數回傳布林值
類型防護函數需要回傳布林表達式。以下是非法的宣告
1function isNumberNoReturn(x: string): x is string {}
1:39-1:49: Cannot declare a type predicate [1] for function [2] because boolean [1] is incompatible with implicitly-returned undefined. [incompatible-return]
1function nonMaybe<V: {...}>(x: ?V): x is V {2 return x; 3}
2:10-2:10: Cannot return `x` because null or undefined [1] is incompatible with boolean [2]. [incompatible-return]2:10-2:10: Cannot return `x` because object type [1] is incompatible with boolean [2]. [incompatible-return]
nonMaybe
的正確版本為
1function nonMaybe<V: {...}>(x: ?V): x is V {2 return !!x;3}
謂詞類型與精緻類型一致
除了上述檢查外,Flow 也會確保宣告的類型防護與函式主體中發生的檢查一致。為此,需要保證兩件事
- 套用回傳表達式謂詞之後,回傳位置的精緻參數類型為防護類型的子類型。例如,下列定義正確
1function numOrStr(x: mixed): x is number | string {2 return (typeof x === "number" || typeof x === "string");3}4
5function numOrStrWithException(x: mixed): x is number | string {6 if (typeof x === "number") {7 return true;8 } else {9 if (typeof x === "string") {10 return true;11 } else {12 throw new Error("");13 }14 }15}
但在下列情況中,Flow 會產生錯誤
1function numOrStrError(x: mixed): x is number | string {2 return (typeof x === "number" || typeof x === "boolean"); 3}
2:36-2:57: Cannot return `((typeof x) === "number") || ((typeof x) === "boolean")` because in the type inferred for type guard parameter `x` [1]: [incompatible-return] Either boolean [2] is incompatible with number [3]. Or boolean [2] is incompatible with string [4].
- 類型防護函式主體中無法重新指派已精緻的參數。因此,下列為錯誤
1function isNumberError1(x: mixed): x is number {2 x = 1;3 return typeof x === "number"; 4}
3:10-3:30: Cannot use type guard parameter `x` [1] because at this return point it is writen to in [2]. [function-predicate]
1function isNumberError2(x: mixed): x is number { 2 function foo() {3 x = 1;4 }5 foo();6 return typeof x === "number";7}
1:36-1:36: Cannot use type guard parameter `x`, because `x` [1] is reassigned in [2]. [function-predicate]
採用
若要使用類型防護,您需要升級您的基礎架構,使其支援語法
flow
和flow-parser
:0.209.1。在 v0.209.1 至 v0.211.1 之間,您需要在.flowconfig
中的[options]
標題下明確啟用它,新增type_guards=true
。prettier
: 3babel
與babel-plugin-syntax-hermes-parser
。請參閱 我們的 Babel 指南 以取得設定說明。eslint
與hermes-eslint
。請參閱 我們的 ESLint 指南 以取得設定說明。