TypeScript 类型守卫详解与实战(十九)
- 编程语言
- 5天前
- 8热度
- 0评论
在 TypeScript 开发中,类型守卫(Type Guards)是一个非常重要的概念。通过类型守卫,我们可以在运行时动态地检查变量的具体类型,从而确保代码的安全性和可靠性。本文将详细介绍 TypeScript 中的几种常见类型守卫,并通过实际示例展示如何在项目中应用这些技术。
什么是类型守卫
类型守卫是 TypeScript 中的一种类型缩小机制。它允许我们在运行时通过特定的条件检查,让编译器能够准确推断出变量的具体类型。通过类型守卫,我们可以安全地访问联合类型变量中特定类型的属性和方法。
为什么需要类型守卫
在 TypeScript 中,一个变量可能被声明为多种类型的联合。当我们需要根据不同类型执行不同操作时,编译器无法自动判断当前的具体类型。类型守卫就是解决这个问题的关键机制。通过类型守卫,我们可以确保在特定条件下访问到正确的类型,从而避免运行时错误。
typeof 类型守卫
typeof 是最常用的类型守卫,主要用于检查原始类型(如 string、number、boolean 等)。它返回一个字符串,表示值的类型。
typeof 基本用法
function processValue(value: string | number): void {
if (typeof value === "string") {
console.log("字符串长度:", value.length);
} else {
console.log("数字翻倍:", value * 2);
}
}
processValue("hello"); // 输出: 字符串长度: 5
processValue(42); // 输出: 数字翻倍: 84typeof 支持的类型
- "string" - 字符串类型
- "number" - 数字类型(包括 NaN 和 Infinity)
- "boolean" - 布尔类型
- "undefined" - 未定义类型
- "object" - 对象类型(注意:数组和 null 也会被识别为 "object")
- "function" - 函数类型
> 注意:typeof 对于数组和 null 都会返回 "object"。如果需要精确区分数组和对象,需要使用其他方式。
instanceof 类型守卫
instanceof 用于检查对象是否是某个类的实例。它通过检查对象的原型链来判断类型。
instanceof 基本用法
class Dog {
bark(): void {
console.log("汪汪汪!");
}
}
class Cat {
meow(): void {
console.log("喵喵喵!");
}
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
makeSound(new Dog()); // 输出: 汪汪汪!
makeSound(new Cat()); // 输出: 喵喵喵!> 说明:instanceof 检查的是对象的原型链,因此它只能用于类实例,不能用于接口和类型别名。
自定义类型守卫
当内置的 typeof 和 instanceof 无法满足需求时,可以创建自定义类型守卫函数。自定义类型守卫使用 value is Type 的返回类型语法。
自定义守卫函数
function isString(value: any): value is string {
return typeof value === "string";
}
function isNumber(value: any): value is number {
return typeof value === "number";
}
function isArray(value: any): value is any[] {
return Array.isArray(value);
}
function processValue(value: string | number | any[]): void {
if (isString(value)) {
console.log("字符串转大写:", value.toUpperCase());
} else if (isNumber(value)) {
console.log("数字格式化:", value.toFixed(2));
} else if (isArray(value)) {
console.log("数组长度:", value.length);
}
}
processValue("hello"); // 输出: 字符串转大写: HELLO
processValue(3.14159); // 输出: 数字格式化: 3.14
processValue([1, 2, 3, 4, 5]); // 输出: 数组长度: 5> 提示:自定义类型守卫的关键是返回类型 value is Type,这是 TypeScript 识别类型守卫的标志。
in 操作符类型守卫
in 操作符可以检查对象是否包含某个属性。在条件判断中使用 in,TypeScript 会自动缩小对象的类型范围。
in 操作符用法
interface A {
a: string;
}
interface B {
b: number;
}
function process(obj: A | B): void {
if ("a" in obj) {
console.log("A 的属性 a:", obj.a);
} else {
console.log("B 的属性 b:", obj.b);
}
}
process({ a: "hello" }); // 输出: A 的属性 a: hello
process({ b: 42 }); // 输出: B 的属性 b: 42可辨识联合与类型守卫
可辨识联合是一种强大的模式,它通过一个公共的“标识”属性来区分联合类型成员。结合 switch 语句或 if 判断,可以实现完整的类型守卫。
可辨识联合实现计算器
interface Circle {
kind: "circle";
radius: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Triangle {
kind: "triangle";
base: number;
height: number;
}
type Shape = Circle | Rectangle | Triangle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "triangle":
return 0.5 * shape.base * shape.height;
}
}
const circle = { kind: "circle" as const, radius: 5 };
const rectangle = { kind: "rectangle" as const, width: 4, height: 6 };
const triangle = { kind: "triangle" as const, base: 3, height: 4 };
console.log("圆形面积:", getArea(circle).toFixed(2)); // 输出: 圆形面积: 78.54
console.log("矩形面积:", getArea(rectangle)); // 输出: 矩形面积: 24
console.log("三角形面积:", getArea(triangle)); // 输出: 三角形面积: 6> 说明:可辨识联合是 TypeScript 中最推荐使用的模式之一。它通过一个公共的字面量属性(通常是 kind 或 type)来区分不同的类型成员,使代码既类型安全又易于维护。
null 和 undefined 检查
处理可能为 null 或 undefined 的值时,直接的相等性检查也是有效的类型守卫。
null 检查
function getLength(str: string | null): number {
if (str !== null) {
return str.length;
}
return 0;
}
console.log(getLength("hello")); // 输出: 5
console.log(getLength(null)); // 输出: 0> 提示:在启用 strictNullChecks 后,建议始终进行 null 检查。可以使用可选链(?.)和空值合并(??)来简化代码。
真值缩小
除了显式的类型检查,TypeScript 还会通过真值断言(Truthiness)来缩小类型范围。
真值缩小
function greet(name?: string): string {
return name && "Hello, " + name;
}
console.log(greet("RUNOOB")); // 输出: Hello, RUNOOB
console.log(greet()); // 输出: Hello, undefined注意事项
- 类型守卫必须在条件分支中使用:只有在使用类型守卫进行条件判断后,TypeScript 才会进行类型缩小。
- 返回类型必须是类型谓词:自定义类型守卫的返回类型必须是 value is Type 格式。
- 可辨识联合是最佳实践:对于复杂的联合类型,建议使用可辨识联合模式。
- 注意类型收窄的完整性:使用 switch 语句时,建议处理所有可能的分支。
> 建议:在处理联合类型时,优先考虑使用可辨识联合模式。它不仅代码更清晰,还能充分利用 TypeScript 的类型推断能力。
总结
类型守卫是 TypeScript 类型系统的重要组成部分,可以帮助我们在运行时动态地检查变量的具体类型,从而确保代码的安全性和可靠性。常见的类型守卫包括:
- typeof: 最常用的方式,适用于原始类型的检查。
- instanceof: 检查对象是否是特定类的实例。
- 自定义守卫: 通过 value is Type 语法实现灵活的类检查。
- in: 检查对象是否包含特定属性。
- 可辨识联合: 推荐的最佳实践模式,通过标识字段区分类型。
- 真值缩小: 利用 JavaScript 的真值判断进行类型收窄。
希望本文能帮助你更好地理解和应用 TypeScript 中的类型守卫,提升代码的质量和安全性。