联合
有时,创建属于一组其他类型之一的类型很有用。例如,你可能想要编写一个接受一组原始值类型的函数。为此 Flow 支持联合类型。
¥Sometimes it's useful to create a type which is one of a set of other types. For example, you might want to write a function which accepts a set of primitive value types. For this Flow supports union types.
1function toStringPrimitives(value: number | boolean | string): string {2 return String(value);3}4
5toStringPrimitives(1); // Works!6toStringPrimitives(true); // Works!7toStringPrimitives('three'); // Works!8
9toStringPrimitives({prop: 'val'}); // Error!10toStringPrimitives([1, 2, 3, 4, 5]); // Error!
联合类型语法
¥Union type syntax
联合类型是由竖线 |
连接的任意数量的类型。
¥Union types are any number of types which are joined by a vertical bar |
.
Type1 | Type2 | ... | TypeN
你还可以添加一个前导竖线,这在将联合类型分解为多行时非常有用。
¥You may also add a leading vertical bar which is useful when breaking union types onto multiple lines.
type Foo =
| Type1
| Type2
| ...
| TypeN
联合类型的每个成员可以是任何类型,甚至是另一个联合类型。
¥Each of the members of a union type can be any type, even another union type.
1type Numbers = 1 | 2;2type Colors = 'red' | 'blue'3
4type Fish = Numbers | Colors;
如果你启用了 Flow 枚举,它们可能是 字面量类型 并集的替代方案。
¥If you have enabled Flow Enums, they may be an alternative to unions of literal types.
联合简写
¥Union shorthands
某些类型 T
与 null
或 void
的并集很常见,因此我们通过使用 ?
前缀提供了一种称为 也许类型 的简写。类型 ?T
等价于 T | null | void
:
¥The union of some type T
with null
or void
is common, so we provide a shorthand called maybe types, by using the ?
prefix. The type ?T
is equivalent to T | null | void
:
1function maybeString(x: ?string) { /* ... */ }2maybeString('hi'); // Works!3maybeString(null); // Works!4maybeString(undefined); // Works!
存在的每个单一类型的并集是 mixed
类型:
¥The union of every single type that exists is the mixed
type:
1function everything(x: mixed) { /* ... */ }2everything(1); // Works!3everything(true); // Works!4everything(null); // Works!5everything({foo: 1}); // Works!6everything(new Error()); // Works!
联合与细化
¥Unions & Refinements
当你有一个联合类型的值时,将其分解并单独处理每个单独的类型通常很有用。使用 Flow 中的联合类型,你可以将值缩小为单一类型。
¥When you have a value which is a union type it's often useful to break it apart and handle each individual type separately. With union types in Flow you can refine the value down to a single type.
例如,如果我们有一个联合类型为 number
、boolean
或 string
的值,我们可以使用 JavaScript 的 typeof
运算符单独处理数字大小写。
¥For example, if we have a value with a union type that is a number
, a
boolean
, or a string
, we can treat the number case separately by using
JavaScript's typeof
operator.
1function toStringPrimitives(value: number | boolean | string) {2 if (typeof value === 'number') {3 return value.toLocaleString([], {maximumSignificantDigits: 3}); // Works!4 }5 // ...6}
通过检查 typeof
的值并测试它是否是 number
,Flow 知道在该块内它只是一个数字。然后我们可以编写代码,将我们的值视为该块内的数字。
¥By checking the typeof
our value and testing to see if it is a number
, Flow
knows that inside of that block it is only a number. We can then write code
which treats our value as a number inside of that block.
联合类型需要一进一出
¥Union types requires one in, but all out
当调用接受联合类型的函数时,我们必须传入这些类型之一。但在函数内部,我们需要处理所有可能的类型。
¥When calling a function that accepts a union type we must pass in one of those types. But inside of the function we are required to handle all of the possible types.
让我们使用 改进 重写该函数以单独处理每种类型。
¥Let's rewrite the function to handle each type individually using refinements.
1function toStringPrimitives(value: number | boolean | string): string {2 if (typeof value === 'number') {3 return String(value);4 } else if (typeof value === 'boolean') {5 return String(value);6 }7 return value; // If we got here, it's a `string`!8}
如果我们不处理值的每种可能类型,Flow 会给我们一个错误:
¥If we do not handle each possible type of our value, Flow will give us an error:
1function toStringPrimitives(value: number | boolean | string): string {2 if (typeof value === 'number') {3 return String(value);4 }5 return value; // Error!6}
不相交对象联合
¥Disjoint Object Unions
Flow 中有一种特殊类型的联合,称为 "不相交的对象并集",它可以与 改进 一起使用。这些不相交的对象联合由任意数量的对象类型组成,每个对象类型都由单个属性标记。
¥There's a special type of union in Flow known as a "disjoint object union" which can be used with refinements. These disjoint object unions are made up of any number of object types which are each tagged by a single property.
例如,假设我们有一个函数,用于在向服务器发送请求后处理来自服务器的响应。当请求成功时,我们将返回一个对象,该对象的 type
属性设置为 'success'
以及我们已更新的 value
。
¥For example, imagine we have a function for handling a response from a server
after we've sent it a request. When the request is successful, we'll get back
an object with a type
property set to 'success'
and a value
that we've
updated.
{type: 'success', value: 23}
当请求失败时,我们将返回一个对象,其中 type
设置为 'error'
以及描述错误的 error
属性。
¥When the request fails, we'll get back an object with type
set to 'error'
and an error
property describing the error.
{type: 'error', error: 'Bad request'}
我们可以尝试用单个对象类型来表达这两个对象。但是,我们很快就会遇到这样的问题:我们知道存在基于 type
属性的属性,但 Flow 不存在。
¥We can try to express both of these objects in a single object type. However,
we'll quickly run into issues where we know a property exists based on the
type
property but Flow does not.
1type Response = {2 type: 'success' | 'error',3 value?: number,4 error?: string5};6
7function handleResponse(response: Response) {8 if (response.type === 'success') {9 const value: number = response.value; // Error!10 } else {11 const error: string = response.error; // Error!12 }13}
试图将这两种不同的类型合并为一种类型只会给我们带来麻烦。
¥Trying to combine these two separate types into a single one will only cause us trouble.
相反,如果我们创建两种对象类型的联合类型,Flow 将能够根据 type
属性知道我们正在使用哪个对象。
¥Instead, if we create a union type of both object types, Flow will be able to
know which object we're using based on the type
property.
1type Response =2 | {type: 'success', value: number}3 | {type: 'error', error: string};4
5function handleResponse(response: Response) {6 if (response.type === 'success') {7 const value: number = response.value; // Works!8 } else {9 const error: string = response.error; // Works!10 }11}
为了使用此模式,联合中的每个对象中都必须有一个键(在上面的示例中为 type
),并且每个对象必须为该键设置不同的 字面量类型(在我们的示例中为字符串 'success'
,并且 字符串 'error'
)。你可以使用任何类型的字面量类型,包括数字和布尔值。
¥In order to use this pattern, there must be a key that is in every object in your union (in our example above, type
),
and every object must set a different literal type for that key (in our example, the string 'success'
, and the string 'error'
).
You can use any kind of literal type, including numbers and booleans.
具有精确类型的不相交对象联合
¥Disjoint object unions with exact types
不相交联合要求你使用单个属性来区分每个对象类型。你无法通过不同的属性来区分两个不同的 不精确的对象。
¥Disjoint unions require you to use a single property to distinguish each object type. You cannot distinguish two different inexact objects by different properties.
1type Success = {success: true, value: boolean, ...};2type Failed = {error: true, message: string, ...};3
4function handleResponse(response: Success | Failed) {5 if (response.success) {6 const value: boolean = response.value; // Error!7 }8}
这是因为在 Flow 中,可以传递具有比不精确对象类型预期更多属性的对象值(因为 宽度子类型)。
¥This is because in Flow it is okay to pass an object value with more properties than the inexact object type expects (because of width subtyping).
1type Success = {success: true, value: boolean, ...};2type Failed = {error: true, message: string, ...};3
4function handleResponse(response: Success | Failed) {5 // ...6}7
8handleResponse({9 success: true,10 error: true,11 value: true,12 message: 'hi'13});
除非这些对象以某种方式彼此冲突,否则无法区分它们。
¥Unless the objects somehow conflict with one another there is no way to distinguish them.
但是,为了解决这个问题,你可以使用精确的对象类型。
¥However, to get around this you could use exact object types.
1type Success = {success: true, value: boolean};2type Failed = {error: true, message: string};3
4type Response = Success | Failed;5
6function handleResponse(response: Response) {7 if (response.success) {8 const value: boolean = response.value;9 } else {10 const message: string = response.message;11 }12}
对于精确的对象类型,我们不能拥有额外的属性,因此对象彼此冲突,我们能够区分哪个是哪个。
¥With exact object types, we cannot have additional properties, so the objects conflict with one another and we are able to distinguish which is which.
不相交元组并集
¥Disjoint tuple unions
与上面解释的不相交对象联合一样,你也可以定义不相交元组联合(Flow 版本支持
¥Like disjoint object unions explained above, you can also define disjoint tuple unions (support in Flow version
≥0.240)。这些是元组类型的联合,其中每个元组都由特定元素标记。例如:
¥). These are unions of tuple types, where each tuple is tagged by a particular element. For example:
1type Response =2 | ['success', number]3 | ['error', string];4
5function handleResponse(response: Response) {6 if (response[0] === 'success') {7 const value: number = response[1]; // Works!8 } else {9 const error: string = response[1]; // Works!10 }11}
此功能对于函数参数(元组)特别有用。请注意使用 元组元素标签 使代码更清晰。
¥This feature is particularly useful for function arguments, which are tuples. Note the use of tuple element labels to make the code more clear.
1function prettyPrint(2 ...args: ['currency', dollars: number, cents: number]3 | ['choice', boolean]4): string {5 switch (args[0]) {6 case 'currency':7 return args[1] + '.' + args[2];8 case 'choice':9 return args[1] ? 'yes' : 'no';10 }11}12// Argument types based on the first arg13prettyPrint("currency", 1, 50); // OK14prettyPrint("choice", true); // OK15
16prettyPrint("currency", 1); // ERROR - missing arg17prettyPrint("currency", true); // ERROR - wrong type arg