联合类型 (Union Types)
联合类型是 TypeScript 中的一种强大特性,它允许你定义一个值可以是多种类型中的一种。这意味着你可以创建更加灵活和表达力更强的类型系统,同时仍然保持静态类型的优点。通过使用联合类型,你可以更好地描述那些在不同情况下可能具有不同类型的变量或函数参数。
定义联合类型
你可以使用竖线 (|) 来分隔多个类型,以创建一个联合类型。下面是一些基本的例子:
// 定义一个联合类型
type ID = number | string;
// 使用联合类型
let userId: ID;
userId = 123; // 允许
userId = "user456"; // 允许在这个例子中,ID 类型既可以是 number 也可以是 string,这使得 userId 变量可以在不同的上下文中接受这两种类型的值。
联合类型的属性访问
当处理联合类型的对象时,TypeScript 只允许你访问那些在所有候选类型中都存在的属性或方法。如果你尝试访问某个仅存在于部分类型的成员,TypeScript 编译器会报错。
interface Bird {
fly: () => void;
}
interface Fish {
swim: () => void;
}
type Pet = Bird | Fish;
function getSmallPet(): Pet {
return {} as Pet; // 假设返回一个 Pet 实例
}
let pet = getSmallPet();
pet.fly(); // 错误:Property 'fly' does not exist on type 'Pet'.
pet.swim(); // 错误:Property 'swim' does not exist on type 'Pet'.要解决这个问题,你需要先确定 pet 的具体类型,然后才能安全地调用特定的方法。这可以通过类型保护(如类型谓词)来实现。
类型保护与缩小
类型保护是一种机制,它允许你在代码中明确指出某个变量属于联合类型中的哪一个具体类型。一旦应用了类型保护,TypeScript 就会“缩小”(narrow down)该变量的类型范围,从而允许你访问更具体的属性或方法。
使用 in 操作符
in 操作符可以用来检查一个属性是否存在于对象上,并且它可以作为类型保护的一部分。
if ("swim" in pet) {
pet.swim(); // 现在 TypeScript 知道 pet 是 Fish 类型
} else {
pet.fly(); // 现在 TypeScript 知道 pet 是 Bird 类型
}使用 typeof 操作符
对于原始类型(如 number、string),你可以使用 typeof 操作符来进行类型保护。
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
// padding is a number here
return Array(padding + 1).join(" ") + value;
}
// padding is a string here
return padding + value;
}使用 instanceof 操作符
当你需要区分类实例时,可以使用 instanceof 操作符。
class Dog {
bark() {}
}
class Cat {
meow() {}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark(); // animal is now known to be a Dog
} else {
animal.meow(); // animal is now known to be a Cat
}
}自定义类型保护函数
你可以编写自定义的类型保护函数,这些函数返回类型谓词,告诉 TypeScript 在什么条件下应该认为变量是特定类型。
function isFish(pet: Pet): pet is Fish {
return (pet as Fish).swim !== undefined;
}
if (isFish(pet)) {
pet.swim(); // TypeScript 知道这里 pet 是 Fish 类型
} else {
pet.fly(); // TypeScript 知道这里 pet 是 Bird 类型
}联合类型的赋值规则
当涉及到联合类型的赋值时,TypeScript 会根据最严格的规则进行检查。也就是说,只有当源类型的所有候选类型都能被目标类型的每个候选类型所容纳时,才允许赋值。
let a: string | number;
let b: string;
a = "hello"; // OK
b = a; // Error: Type 'string | number' is not assignable to type 'string'.联合类型的交叉类型
联合类型可以与其他类型操作符(如交叉类型)结合使用,以创建更复杂的类型结构。
type Either2dOr3d =
| { x: number; y: number }
| { x: number; y: number; z: number };
function setCoordinate(coord: Either2dOr3d) {
coord.x = 10;
coord.y = 20;
if ("z" in coord) {
coord.z = 30;
}
}总结
联合类型是 TypeScript 提供的一个强大工具,它允许你定义一个值可以是多种类型中的一种。理解联合类型的工作原理及其与类型保护的结合使用,可以帮助你在编写代码时充分利用 TypeScript 的灵活性,同时保持良好的类型安全性。