告别类型判断烦恼:TypeScript 联合类型与类型守卫实战
在日常开发中,我们常常需要处理可能是多种类型的变量。例如,一个参数可以是 string 或 number,一个 API 响应可能成功或失败。在纯 JavaScript 中,处理这种不确定性需要大量的 typeof 和属性检查,既繁琐又容易出错。
TypeScript 的联合类型(Union Types) 和类型守卫(Type Guards) 正是为此而生的强大组合,它们能让我们在享受灵活性的同时,保证代码的类型安全。
一、 联合类型:表达不确定性
联合类型使用 | 运算符,表示一个值可以是几种类型之一。
function printId(id: number | string) {
console.log(`Your ID is: ${
id}`);
}
printId(101); // OK
printId("ABC202"); // OK
printId({
key: "value" }); // Error!
现在,id 可以是数字或字符串,但直接对其操作(比如调用 toUpperCase())会报错,因为 TypeScript 不知道在运行时它具体是哪种类型。
二、 类型守卫:缩小类型范围
类型守卫是能够在运行时检查类型的表达式,它帮助 TypeScript 在特定代码块中收窄(Narrow) 变量的类型。
1. typeof 守卫:
处理基本类型时最常用。
function printId(id: number | string) {
if (typeof id === "string") {
// 在此块内,TypeScript 知道 id 是 string
console.log(id.toUpperCase());
} else {
// 在此块内,id 是 number
console.log(id.toFixed(2));
}
}
2. 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) {
// 收窄为 Circle
return Math.PI * shape.radius ** 2;
} else {
// 收窄为 Square
return shape.sideLength ** 2;
}
}
3. 自定义类型守卫函数:
对于更复杂的逻辑,可以定义一个返回类型为 arg is Type 的函数。
function isCircle(shape: Shape): shape is Circle {
return shape.kind === "circle";
}
function getArea(shape: Shape) {
if (isCircle(shape)) {
// 因为 isCircle 是类型守卫,这里 shape 被收窄为 Circle
return Math.PI * shape.radius ** 2;
}
// ... 处理 Square
}
总结
联合类型与类型守卫是 TypeScript 类型系统的核心利器。它们允许我们清晰地表达业务逻辑中的不确定性,并通过编译时的强制检查,将许多潜在的运行时错误消灭在编码阶段。善用它们,能极大提升代码的健壮性和开发体验。