联合类型(Union Types)
TypeScript 的类型系统允许你使用多种运算符从现有类型构建新类型。现在我们已经学会了如何编写一些类型,是时候以更有趣的方式组合它们了。
定义联合类型
你可能首先遇到的组合类型方式是联合类型(Union Type) 。联合类型是由两个或多个其他类型组成的类型,表示的值可以是这些类型中的任何一个。我们称这些类型为联合类型的成员(members) 。
让我们编写一个可以处理 string 或 number 类型的函数:
function printId(id: number | string) {
console.log("Your ID is: " + id);
}
// OK
printId(101);
// OK
printId("202");
// Error
// Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.
printId({ myID: 22342 });
联合类型的分隔符(|)允许出现在第一个元素之前,因此你也可以这样写:
// 这样写主要是为了代码看起来更容易阅读
function printTextOrNumberOrBool(
textOrNumberOrBool:
| string
| number
| boolean
) {
console.log(textOrNumberOrBool);
}
处理联合类型
提供符合联合类型的值很简单——只需提供匹配联合成员之一的类型即可。但是,如果你有一个联合类型的值,该如何对其进行操作呢?
TypeScript 仅允许在所有联合成员都支持的情况下执行某个操作。例如,在 string | number 这样的联合类型中,你无法使用仅适用于 string 的方法:
// @errors: 2339
// Property 'toUpperCase' does not exist on type 'string | number'. Property 'toUpperCase' does not exist on type 'number'.
function printId(id: number | string) {
console.log(id.toUpperCase());
}
解决方法是使用代码来缩小(narrow)联合类型,这与在 JavaScript 中(不使用类型注解)进行类型判断的方式相同。当 TypeScript 能根据代码结构推断出更具体的类型时,就会发生类型缩小(narrowing)。
例如,TypeScript 知道只有 string 类型的值,其 typeof 结果才是 "string":
function printId(id: number | string) {
if (typeof id === "string") {
/ 在这个分支中,id 的类型是 'string'
console.log(id.toUpperCase());
} else {
// 在这里,id 的类型是 'number'
console.log(id);
}
}
另一个例子是使用像 Array.isArray 这样的函数来缩小联合类型:
function welcomePeople(x: string[] | string) {
if (Array.isArray(x)) {
// 在这里:'x' 是 'string[]' 类型
console.log("Hello, " + x.join(" and "));
} else {
// 在这里:'x' 是 'string' 类型
console.log("Welcome lone traveler " + x);
}
}
请注意,在 else 分支中,我们不需要做任何特殊处理——如果 x 不是 string[],那么它一定是 string 类型。
有时你会遇到一个联合类型,其中所有成员都有共同的属性。例如,数组和字符串都有 slice 方法。如果联合类型中的每个成员都有一个共同的属性,你可以在不进行类型缩小的情况下直接使用该属性:
// 返回类型推断为 number[] | string
function getFirstThree(x: number[] | string) {
return x.slice(0, 3);
}
这可能会让人感到困惑,因为联合类型看起来好像包含了这些类型属性的交集。这并不是偶然的——“联合”(Union)这个名称源自类型理论。number | string 的联合类型是通过将每个类型的值的联合来构成的。请注意,给定两个集合,其中每个集合都有一些相应的事实,只有这些事实的交集适用于这两个集合的联合。例如,如果我们有一个房间,里面是戴帽子的高个子,然后另一个房间是讲西班牙语的人也戴帽子,合并这两个房间后,我们能知道的唯一事实是,每个人都必须戴帽子。