联合类型
联合类型由一组有序的成员类型构成。联合类型表示一个值的类型可以为若干种类型之一。例如,联合类型 string | number
表示一个值的类型既可以为 string
类型也可以为 number
类型。
联合类型通过联合类型字面量来定义。
联合类型字面量
联合类型由两个或两个以上的成员类型构成,各成员类型之间使用竖线符号 |
分隔。示例如下:
type NumericType = number | bigint;
此例中,定义了一个名为 NumericType
的联合类型。该联合类型由两个成员类型组成,即 number
类型和 bigint
类型。若一个值既可能为 number
类型又可能为 bigint
类型,那么我们说该值的类型为联合类型 number | bigint
。
联合类型的成员类型可以为任意类型,如原始类型、数组类型、对象类型,以及函数类型等。示例如下:
type T = boolean | string[] | { x: number } | (() => void);
如果联合类型中存在相同的成员类型,那么相同的成员类型将被合并为单一成员类型。例如,下例中的类型别名 T0
和 T1
都表示 boolean
类型,类型别名 T2
和 T3
都表示同一种联合类型 boolean | string
:
type T0 = boolean; // boolean
type T1 = boolean | boolean; // boolean
type T2 = boolean | string; // boolean | string
type T3 = boolean | string | boolean; // boolean | string
联合类型是有序的成员类型的集合。在绝大部分情况下,成员类型满足类似于数学中的 加法交换律,即改变成员类型的顺序不影响联合类型的结果类型。下例中,虽然联合类型 T0
与 T1
中成员类型的顺序不同,但是 T0
与 T1
表示相同的类型:
type T0 = string | number;
type T1 = number | string;
联合类型中的类型成员同样满足类似于数学中的 加法结合律。对部分类型成员使用分组运算符不影响联合类型的结果类型。例如,下例中的 T0
和 T1
类型是同一个类型:
type T0 = (boolean | string) | number;
type T1 = boolean | (string | number);
联合类型的成员类型可以进行化简。假设有联合类型 U = T0 | T1
,如果 T1
是 T0
的子类型,那么可以将类型成员 T1
从联合类型 U
中消去。最后,联合类型 U
的结果类型为 U = T0
。例如,有联合类型 boolean | true | false
。其中,true
类型和 false
类型是 boolean
类型的子类型,因此可以将 true
类型和 false
类型从联合类型中消去。最终,联合类型 boolean | true | false
的结果类型为 boolean
类型。示例如下:
// 'true' 和 'false' 类型是 'boolean' 类型的子类型
type T0 = boolean | true | false;
// 所以T0等同于 T1
type T1 = boolean;
关于子类型的详细介绍请参考 7.1 节。
联合类型的类型成员
像接口类型一样,联合类型作为一个整体也可以有类型成员,只不过联合类型的类型成员是由其成员类型决定的。
属性签名
若联合类型 U
中的每个成员类型都包含一个同名的属性签名 M
,那么联合类型 U
也包含属性签名 M
。例如,有如下定义的 Circle
类型与 Rectangle
类型,以及由这两个类型构成的联合类型 Shape
:
interface Circle {
area: number;
radius: number;
}
interface Rectangle {
area: number;
width: number;
height: number;
}
type Shape = Circle | Rectangle;
此例中,因为 Circle
类型与 Rectangle
类型均包含名为 area
的属性签名类型成员,所以联合类型 Shape
也包含名为 area
的属性签名类型成员。因此,允许访问 Shape
类型上的 area
属性。示例如下:
type Shape = Circle | Rectangle;
declare const s: Shape;
s.area; // number
因为 radius
、width
和 height
类型成员不是 Circle
类型和 Rectangle
类型的共同类型成员,因此它们不是 Shape
联合类型的类型成员。示例如下:
type Shape = Circle | Rectangle;
declare const s: Shape;
s.radius; // 错误
s.width; // 错误
s.height; // 错误
对于联合类型的属性签名,其类型为所有成员类型中该属性类型的联合类型。例如,下例中联合类型 Circle | Rectangle
具有属性签名 area
,其类型为 Circle
类型中 area
属性的类型和 Rectangle
类型中 area
属性的类型组成的联合类型,即 bigint |number
类型。示例如下:
interface Circle {
area: bigint;
}
interface Rectangle {
area: number;
}
declare const s: Circle | Rectangle;
s.area; // bigint | number
如果联合类型的属性签名在某个成员类型中是可选属性签名,那么该属性签名在联合类型中也是可选属性签名;否则,该属性签名在联合类型中是必选属性签名。示例如下:
interface Circle {
area: bigint;
}
interface Rectangle {
area?: number;
}
declare const s: Circle | Rectangle;
s.area; // bigint | number | undefined
此例中,area
属性在 Rectangle
类型中是可选的属性。因此,在联合类型 Circle |Rectangle
中,area
属性也是可选属性。
索引签名
索引签名包含两种,即字符串索引签名和数值索引签名。在联合类型中,这两种索引签名具有相似的行为。
如果联合类型中每个成员都包含字符串索引签名,那么该联合类型也拥有了字符串索引签名,字符串索引签名中的索引值类型为每个成员类型中索引值类型的联合类型;否则,该联合类型没有字符串索引签名。例如,下例中的联合类型 T
相当于接口类型 T0T1
:
interface T0 {
[prop: string]: number;
}
interface T1 {
[prop: string]: bigint;
}
type T = T0 | T1;
interface T0T1 {
[prop: string]: number | bigint;
}
如果联合类型中每个成员都包含数值索引签名,那么该联合类型也拥有了数值索引签名,数值索引签名中的索引值类型为每个成员类型中索引值类型的联合类型;否则,该联合类型没有数值索引签名。例如,下例中的联合类型 T
相当于接口类型 T0T1
:
interface T0 {
[prop: number]: number;
}
interface T1 {
[prop: number]: bigint;
}
type T = T0 | T1;
interface T0T1 {
[prop: number]: number | bigint;
}
调用签名与构造签名
如果联合类型中每个成员类型都包含相同参数列表的调用签名,那么联合类型也拥有了该调用签名,其返回值类型为每个成员类型中调用签名返回值类型的联合类型;否则,该联合类型没有调用签名。例如,下例中的联合类型 T
相当于接口类型 T0T1
:
interface T0 {
(name: string): number;
}
interface T1 {
(name: string): bigint;
}
type T = T0 | T1;
interface T0T1 {
(name: string): number | bigint;
}
同理,如果联合类型中每个成员都包含相同参数列表的构造签名,那么该联合类型也拥有了构造签名,其返回值类型为每个成员类型中构造签名返回值类型的联合类型;否则,该联合类型没有构造签名。例如,下例中的联合类型 T
相当于接口类型 T0T1
:
interface T0 {
new (name: string): Date;
}
interface T1 {
new (name: string): Error;
}
type T = T0 | T1;
interface T0T1 {
new (name: string): Date | Error;
}