Skip to content

类型守卫 (Type Guards)

类型守卫是 TypeScript 中用于缩小联合类型范围的一种机制。通过类型守卫,你可以明确告诉 TypeScript 编译器,在特定条件下,变量的类型是什么。这不仅提高了代码的安全性和可读性,还减少了不必要的类型断言。

类型守卫的作用

类型守卫的主要作用是在运行时检查一个值是否属于某个特定类型,并在编译时利用这些信息来缩小联合类型的范围。这使得你可以在不同的分支中安全地访问该类型特有的属性或方法。

实现类型守卫的方式

TypeScript 提供了多种方式来实现类型守卫:

  1. typeof 操作符:用于检查基本类型的值。
  2. instanceof 操作符:用于检查对象是否是某个类的实例。
  3. 自定义类型守卫函数:返回一个类型谓词(type predicate),明确告诉 TypeScript 编译器应如何缩小类型。
  4. in 操作符:用于检查对象是否具有某个属性。
  5. 字面量类型检查:结合字面量类型和控制流分析来缩小类型范围。

1. typeof 类型守卫

typeof 是一种简单且常用的方式来创建类型守卫,主要用于基本类型如 stringnumberboolean 等。

typescript
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 用于检查对象是否是某个类的实例,特别适合处理继承关系或需要区分不同类的对象。

typescript
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

typescript
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 操作符用于检查对象是否具有某个属性,可以作为类型保护的一部分来缩小联合类型的范围。

typescript
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 语句或其他条件表达式一起使用,以缩小联合类型的范围。

typescript
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 stringvalue is number

  • 性能考虑:虽然类型守卫非常有用,但也要注意不要过度使用它们,以免引入不必要的运行时开销。

总结

类型守卫是 TypeScript 提供的一个强大工具,它允许你编写函数来缩小联合类型的范围。理解如何定义和使用这些守卫可以帮助你在编写代码时充分利用 TypeScript 的静态类型检查能力,提高代码的安全性和可读性。

Released under the MIT License.