Skip to main content

联合

有时,创建属于一组其他类型之一的类型很有用。例如,你可能想要编写一个接受一组原始值类型的函数。为此 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

某些类型 Tnullvoid 的并集很常见,因此我们通过使用 ? 前缀提供了一种称为 也许类型 的简写。类型 ?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.

例如,如果我们有一个联合类型为 numberbooleanstring 的值,我们可以使用 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