- 引用:
- 联合类型(Union Types)
- 联合类型表示取值可以为多种类型中的一种。
- 联合类型代表了一组类型的可用集合,只要最终赋值的类型属于联合类型的成员之一,就可以认为符合这个联合类型。
- 联合类型使用
|
分隔每个类型 let a: string | number
表示的含义是变量a
的类型可以是string
类型也可以是number
类型
let a: string | number // Union Types
a = 1 // ok
a = '1' // ok
type Mixed = (...regs: any[]) => void | null
// ok
let fn1: Mixed = null
// ok
const fn2: Mixed = () => {
console.log('hello')
}
- 当 ts 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
- 联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型
- 类型保护机制
- 如果一个变量的类型可能会多种情况,通过触发 ts 的类型保护机制来明确变量的具体类型
- 在使用联合类型时,一个变量的类型有多种可能的情况,可结合类型保护机制来区分变量的具体类型
- 比如
typeof
、instanceof
…… 都可以触发 ts 的类型保护机制
function getLength(x: string | number): number {
return x.length; // error
// 会报错:因为 string 有 length,number 没有 length
}
function getLength(x: string | number): number {
return x.toString().length // ok
// 不会报错,因为 toString 是 string 和 number 共有的成员
}
function getLength(x: string | number): number {
// 使用 typeof 触发类型保护机制
if (typeof x === 'string') {
// 程序执行到这里,可以明确 x 的类型一定是 string
return x.length
} else {
// 程序执行到这里,可以明确 x 的类型一定是 number
return x.toString().length
}
}
let a: number | string
a // ts 无法明确 a 的类型,ts 认为 a 可能是 number 或 string
a = 1
a // 程序执行到这里,ts 能明确 a 是 number 类型
a = '1'
a // 程序执行到这里,ts 能明确 a 是 string 类型
- 你可以在联合类型中进一步嵌套联合类型,但这些嵌套的联合类型最终都会被展平到第一级中
type Union1 = (string | number) | boolean;
// 等效写法 👇
type Union2 = string | number | boolean;
type Union3 = ((string | number) | boolean) | (null | undefined);
// 等效写法 👇
type Union4 = string | number | boolean | null | undefined;
- 【应用】通过多个对象类型的联合,来实现手动的互斥属性,即这一属性如果有字段 1,那就没有字段 2
interface Tmp {
user: {
vip: true
expires: string
} | {
vip: false
promotion: string
}
}
declare var tmp: Tmp;
if (tmp.user.vip) {
console.log(tmp.user.expires);
// ts 能够推断出 tmp.user 的类型:
// (property) Tmp.user: {
// vip: true;
// expires: string;
// }
} else if (tmp.user.vip === false) {
console.log(tmp.user.promotion);
// ts 能够推断出 tmp.user 的类型:
// tmp.user
// Tmp.user: {
// vip: false;
// promotion: string;
// }
}
注解
这是 ts 的类型收窄(type narrowing)或称为类型判别(type discrimination)的一个示例。ts 能够根据运行时检查来收窄或判别一个值的类型。在上述程序中,我们有一个 Tmp
接口,它描述了一个 user
属性,该属性可以有两种不同的形态。
- 当
vip
是true
时,它有一个expires
属性。 - 当
vip
是false
时,它有一个promotion
属性。
当我们在程序中检查 tmp.user.vip
的值时,ts 能够收窄 tmp.user
的可能类型。
在 if (tmp.user.vip)
块中,ts 已经确定 vip
为 true
,所以它可以确定 tmp.user
的类型为 { vip: true; expires: string; }
。这就是为什么我们可以安全地访问 tmp.user.expires
而不会收到类型错误。
在 else if (tmp.user.vip === false)
块中,ts 知道 vip
为 false
,所以它可以确定 tmp.user
的类型为 { vip: false; promotion: string; }
。这就是为什么我们可以安全地访问 tmp.user.promotion
而不会收到类型错误。
这种基于运行时检查来收窄类型的能力是 ts 的一个强大特性,它允许开发者编写更安全、更容易理解的代码。