TS 类型保护
在 TypeScript 中,类型保护(Type Guards)是一种确保代码在运行时根据某些条件来细化变量类型的技术。类型保护的目的是让编译器能够更准确地了解某些变量在特定代码块中的具体类型,从而提供更严格的类型检查和更智能的代码补全。
实质上 TypeScript 通过类型保护来缩小类型范围,让编译器可以了解在特定条件下变量的类型。
typeof 类型保护
typeof 操作符可以用来判断一个变量的基本类型。适用于 string、number、boolean 和 symbol 类型。
function padLeft(value: string | number, padding: string | number): string {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`'${typeof padding}'.`);
}
在这个例子中,typeof padding 检查用于确定 padding 是 number 还是 string,然后根据不同的类型执行不同的操作。
为什么 typeof 不适用于对象类型?答案是因为所有对象类型(包括数组、函数等)的 typeof 结果都是 'object',因此无法通过 typeof 来细化具体的对象类型。
instanceof 类型保护
instanceof 操作符用来检测对象是否是某个类的实例。适用于类和对象的类型保护。
class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
function makeNoise(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else if (animal instanceof Cat) {
animal.meow();
}
}
在这个代码示例中,instanceof 检查用于确定 animal 是 Dog 还是 Cat 的实例。
in 操作符类型保护
in 操作符可以用来检查对象是否包含某个属性。适用于对象的类型保护。
interface Car {
drive(): void;
}
interface Boat {
sail(): void;
}
function moveVehicle(vehicle: Car | Boat) {
if ("drive" in vehicle) {
vehicle.drive();
} else {
vehicle.sail();
}
}
在上面这个代码示例中,in 操作符检查 vehicle 是否包含 drive 属性。
字面量类型守卫
使用字面量类型进行类型保护,可以在类型范围较小且固定的情况下使用。
type Pet = "cat" | "dog";
function getPetSound(pet: Pet) {
if (pet === "cat") {
return "Meow";
} else if (pet === "dog") {
return "Woof";
}
}
在这个例子中,通过比较字面量类型 cat 和 dog 来进行类型保护。
自定义类型保护
自定义类型保护函数通过返回一个类型谓词来告诉 TypeScript 变量的具体类型。类型谓词的格式为 param is Type。
自定义类型保护的步骤
-
定义类型保护函数:函数返回一个布尔值,并使用类型谓词表示参数在某种条件下的具体类型。
-
在代码中使用类型保护函数:在 if 语句中调用类型保护函数,根据返回结果进行类型细化。
假设我们有两个接口 Bird 和 Fish,并希望根据某个属性来区分它们。
interface Bird {
fly(): void;
}
interface Fish {
swim(): void;
}
我们可以定义一个类型保护函数 isBird 来判断一个对象是否是 Bird 类型。
function isBird(pet: Bird | Fish): pet is Bird {
return (pet as Bird).fly !== undefined;
}
在实际代码中,我们可以使用这个类型保护函数来细化类型:
function move(pet: Bird | Fish) {
if (isBird(pet)) {
pet.fly(); // 在这个块中,TypeScript 知道 pet 是 Bird 类型
} else {
pet.swim(); // 在这个块中,TypeScript 知道 pet 是 Fish 类型
}
}
接下来我们来看一个更复杂的例子,它涉及多个类型和属性检查:
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function isSquare(shape: Shape): shape is Square {
return shape.kind === "square";
}
function isRectangle(shape: Shape): shape is Rectangle {
return shape.kind === "rectangle";
}
function area(shape: Shape): number {
if (isSquare(shape)) {
return shape.size * shape.size;
} else if (isRectangle(shape)) {
return shape.width * shape.height;
} else {
// 在这个块中,TypeScript 知道 shape 是 Circle 类型
return Math.PI * shape.radius * shape.radius;
}
}
在这个例子中,我们定义了三个类型保护函数 isSquare、isRectangle 和 isCircle,分别用于判断 shape 是否是 Square、Rectangle 或 Circle 类型。然后在 area 函数中,我们使用这些类型保护函数来细化 shape 的类型,从而在不同类型的代码块中执行不同的逻辑。
总结
类型保护是 TypeScript 中一种非常强大的工具,通过类型保护可以在运行时提供更精确的类型信息,减少类型错误。并且在开发阶段可以提供更智能的代码补全和类型提示,提高开发效率。
转载自:https://juejin.cn/post/7392249184122437668