类型守卫 (Type Guards)
类型守卫是 TypeScript 中用于缩小联合类型范围的一种机制。通过类型守卫,你可以明确告诉 TypeScript 编译器,在特定条件下,变量的类型是什么。这不仅提高了代码的安全性和可读性,还减少了不必要的类型断言。
类型守卫的作用
类型守卫的主要作用是在运行时检查一个值是否属于某个特定类型,并在编译时利用这些信息来缩小联合类型的范围。这使得你可以在不同的分支中安全地访问该类型特有的属性或方法。
实现类型守卫的方式
TypeScript 提供了多种方式来实现类型守卫:
typeof操作符:用于检查基本类型的值。instanceof操作符:用于检查对象是否是某个类的实例。- 自定义类型守卫函数:返回一个类型谓词(type predicate),明确告诉 TypeScript 编译器应如何缩小类型。
in操作符:用于检查对象是否具有某个属性。- 字面量类型检查:结合字面量类型和控制流分析来缩小类型范围。
1. typeof 类型守卫
typeof 是一种简单且常用的方式来创建类型守卫,主要用于基本类型如 string、number、boolean 等。
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
return padding + value;
}在这个例子中,typeof padding === "number" 是一个类型守卫,它告诉 TypeScript 在这个条件成立时,padding 的类型是 number。
2. instanceof 类型守卫
instanceof 用于检查对象是否是某个类的实例,特别适合处理继承关系或需要区分不同类的对象。
class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark(); // TypeScript knows animal is Dog here
} else {
animal.meow(); // TypeScript knows animal is Cat here
}
}在这个例子中,animal instanceof Dog 是一个类型守卫,它告诉 TypeScript 在这个条件成立时,animal 的类型是 Dog。
3. 自定义类型守卫函数
自定义类型守卫函数是最灵活的方式之一,它允许你编写逻辑来确定变量的具体类型。这种函数返回一个类型谓词,形式为 parameterName is Type。
interface Bird {
fly(): void;
}
interface Fish {
swim(): void;
}
function isBird(animal: Bird | Fish): animal is Bird {
return (animal as Bird).fly !== undefined;
}
function move(animal: Bird | Fish) {
if (isBird(animal)) {
animal.fly(); // TypeScript knows animal is Bird here
} else {
animal.swim(); // TypeScript knows animal is Fish here
}
}在这个例子中,isBird 函数是一个自定义类型守卫,它通过检查 fly 方法是否存在来判断 animal 是否为 Bird 类型。
4. in 类型守卫
in 操作符用于检查对象是否具有某个属性,可以作为类型保护的一部分来缩小联合类型的范围。
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape) {
if ("radius" in shape) {
return Math.PI * shape.radius ** 2; // TypeScript knows shape is Circle here
} else {
return shape.sideLength ** 2; // TypeScript knows shape is Square here
}
}在这个例子中,"radius" in shape 是一个类型守卫,它告诉 TypeScript 在这个条件成立时,shape 的类型是 Circle。
5. 字面量类型检查
结合字面量类型和控制流分析,可以使代码更加安全和精确。字面量类型可以与 if 语句或其他条件表达式一起使用,以缩小联合类型的范围。
function getPadding(value: string, padding: "left" | "right") {
if (padding === "left") {
return `Left padding: ${value}`;
} else {
return `Right padding: ${value}`;
}
}在这个例子中,padding === "left" 是一个类型守卫,它告诉 TypeScript 在这个条件成立时,padding 的类型是 "left"。
注意事项
类型谓词格式:确保你的类型守卫函数返回的是正确的类型谓词格式,即
parameterName is Type。类型兼容性:类型守卫中的类型必须与联合类型中的某个候选类型兼容。例如,如果你有一个
string | number的联合类型,那么类型守卫应该返回value is string或value is number。性能考虑:虽然类型守卫非常有用,但也要注意不要过度使用它们,以免引入不必要的运行时开销。
总结
类型守卫是 TypeScript 提供的一个强大工具,它允许你编写函数来缩小联合类型的范围。理解如何定义和使用这些守卫可以帮助你在编写代码时充分利用 TypeScript 的静态类型检查能力,提高代码的安全性和可读性。