自定义类型守卫 (Custom Type Guards)
自定义类型守卫是 TypeScript 中一种强大的工具,它允许你编写函数来缩小联合类型的范围。通过返回一个类型谓词(type predicate),你可以告诉 TypeScript 编译器在特定条件下变量的类型是什么。这不仅提高了代码的安全性和可读性,还减少了不必要的类型断言。
定义自定义类型守卫
要创建一个自定义类型守卫,你需要编写一个函数,该函数返回一个类型谓词。类型谓词的形式为 parameterName is Type,其中 parameterName 是函数参数的名字,而 Type 是你希望缩小后的类型。
function isString(value: any): value is string {
return typeof value === "string";
}在这个例子中,isString 函数接受一个任意类型的参数,并检查其是否为字符串。如果条件成立,则返回 true 并告知 TypeScript 该值是一个 string 类型;否则返回 false。
使用自定义类型守卫
一旦定义了类型守卫,你就可以在条件语句中使用它来缩小联合类型的范围:
function padLeft(value: string, padding: string | number) {
if (isString(padding)) {
// padding is a string here
return padding + value;
}
// padding is a number here
return Array(padding + 1).join(" ") + value;
}在这个例子中,padding 的类型是 string | number 的联合类型。通过 isString(padding) 的检查,TypeScript 编译器知道在这个分支中 padding 是 string 类型,因此可以安全地执行相应的操作。
更复杂的类型守卫
自定义类型守卫不仅可以用于基本类型,还可以用于更复杂的情况,如接口或类实例:
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 函数接受一个可能是 Bird 或 Fish 的参数,并检查其是否具有 fly 方法。如果条件成立,则返回 true 并告知 TypeScript 该值是一个 Bird 类型;否则返回 false。
组合多个类型守卫
你还可以组合多个类型守卫来处理更复杂的逻辑:
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function isCircle(shape: Shape): shape is Circle {
return shape.kind === "circle";
}
function getArea(shape: Shape) {
if (isCircle(shape)) {
return Math.PI * shape.radius ** 2; // shape is Circle here
} else {
return shape.sideLength ** 2; // shape is Square here
}
}在这个例子中,getArea 函数根据传入的 Shape 类型的不同,分别计算圆形和正方形的面积。通过 isCircle(shape) 的检查,TypeScript 编译器知道在这个分支中 shape 是 Circle 类型,因此可以安全地访问 radius 属性。
注意事项
类型谓词格式:确保你的类型守卫函数返回的是正确的类型谓词格式,即
parameterName is Type。类型兼容性:类型守卫中的类型必须与联合类型中的某个候选类型兼容。例如,如果你有一个
string | number的联合类型,那么类型守卫应该返回value is string或value is number。性能考虑:虽然类型守卫非常有用,但也要注意不要过度使用它们,以免引入不必要的运行时开销。
总结
自定义类型守卫是 TypeScript 提供的一个强大工具,它允许你编写函数来缩小联合类型的范围。理解如何定义和使用这些守卫可以帮助你在编写代码时充分利用 TypeScript 的静态类型检查能力,提高代码的安全性和可读性。