类型守卫
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
。
我们还编写了一个用户定义的类型守卫函数 isA
,它定义在类型 AorB
的对象上。当其输入的 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]
在条件语句 if (isA(x))
的 then 分支中,x
的类型将为 A
。
使用 Array.filter
细化
Flow 识别您在类型为 Array<T>
的数组上调用 filter
时,使用回调函数持有类型守卫,其类型为 (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<Success>
,它与预期的返回类型 Array<Error>
不兼容。
在 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]
它不能是解构模式中绑定的参数,也不能是剩余参数
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 指南。