联合类型(Union Types)
输入的值可能是多种类型中的一种。
有时创建一组类型并以其中一个类型为准是很有用的。 例如,你可能希望编写一个接受一组原始值类型的函数。
// @flow
function toStringPrimitives(value: number | boolean | string) {
return String(value);
}
toStringPrimitives(1); // Works!
toStringPrimitives(true); // Works!
toStringPrimitives('three'); // Works!
// $ExpectError
toStringPrimitives({ prop: 'val' }); // Error!
// $ExpectError
toStringPrimitives([1, 2, 3, 4, 5]); // Error!
联合类型语法
联合类型是通过竖线|
连接的任意数量的类型。
Type1 | Type2 | ... | TypeN
或者
type Foo =
| Type1
| Type2
| ...
| TypeN
联合类型的每个成员可以是任何类型,甚至是另一个联合类型。
type Numbers = 1 | 2;
type Colors = 'red' | 'blue'
type Fish = Numbers | Colors;
传入与处理
当调用接受联合类型的函数时,我们必须传入其中的一种类型。但是在我们的函数里面,我们需要处理所有可能的类型。
// @flow
// $ExpectError
function toStringPrimitives(value: number | boolean | string): string { // Error!
if (typeof value === 'number') {
return String(value);
} else if (typeof value === 'boolean') {
return String(value);
}
}
这里我们没有处理每一种情况(漏掉了string
类型的处理),Flow抛出异常。
联合与改进
当你有一个联合类型的值时,把它分开并分别处理每个类型通常是有用的。使用Flow中的联合类型,可以将值精简为单个类型。上面的例子中我们可以使用JavaScript的typeof
运算符分别处理数字大小写。
// @flow
function toStringPrimitives(value: number | boolean | string) {
if (typeof value === 'number') {
return value.toLocaleString([], { maximumSignificantDigits: 3 }); // Works!
}
// ...
}
不相交联合
例如,假设在发送请求后,我们有一个处理服务器响应的函数。当请求成功时,我们将返回一个成功的属性。
{ success: true, value: false };
当请求失败时,我们将返回一个失败的属性。
{ success: false, error: 'Bad request' };
我们可以尝试在单个对象类型中表示这两个对象。
// @flow
type Response = {
success: boolean,
value?: boolean,
error?: string
};
function handleResponse(response: Response) {
if (response.success) {
// $ExpectError
var value: boolean = response.value; // Error!
} else {
// $ExpectError
var error: string = response.error; // Error!
}
}
试图将这两种不同的类型合并成一个单一的类型并使用时,Flow会抛出异常。相反,如果我们创建了两种对象类型的联合类型,Flow将能够根据success属性知道我们正在使用哪个对象。
// @flow
type Success = { success: true, value: boolean };
type Failed = { success: false, error: string };
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value; // Works!
} else {
var error: string = response.error; // Works!
}
}
确切类型的不相交联合
不相交联合要求你通过单个属性区分不同对象。
// @flow
type Success = { success: true, value: boolean };
type Failed = { error: true, message: string };
function handleResponse(response: Success | Failed) {
if (response.success) {
// $ExpectError
var value: boolean = response.value; // Error!
}
}
这由于宽度子类型的缘故,Flow允许传递一个比预期的拥有更多属性的对象。
// @flow
type Success = { success: true, value: boolean };
type Failed = { error: true, message: string };
function handleResponse(response: Success | Failed) {
// ...
}
handleResponse({
success: true,
error: true,
value: true,
message: 'hi'
});
除非对象彼此间差异,否则无法区分。所以,只有使用确切的对象类型才能解决问题。
// @flow
type Success = {| success: true, value: boolean |};
type Failed = {| error: true, message: string |};
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value;
} else {
var message: string = response.message;
}
}
有了确切的对象类型,我们不能添加额外的属性,所以对象彼此间差异,我们能够区分哪个是哪个。