Skip to content

自定义类型守卫 (Custom Type Guards)

自定义类型守卫是 TypeScript 中一种强大的工具,它允许你编写函数来缩小联合类型的范围。通过返回一个类型谓词(type predicate),你可以告诉 TypeScript 编译器在特定条件下变量的类型是什么。这不仅提高了代码的安全性和可读性,还减少了不必要的类型断言。

定义自定义类型守卫

要创建一个自定义类型守卫,你需要编写一个函数,该函数返回一个类型谓词。类型谓词的形式为 parameterName is Type,其中 parameterName 是函数参数的名字,而 Type 是你希望缩小后的类型。

typescript
function isString(value: any): value is string {
  return typeof value === "string";
}

在这个例子中,isString 函数接受一个任意类型的参数,并检查其是否为字符串。如果条件成立,则返回 true 并告知 TypeScript 该值是一个 string 类型;否则返回 false

使用自定义类型守卫

一旦定义了类型守卫,你就可以在条件语句中使用它来缩小联合类型的范围:

typescript
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 编译器知道在这个分支中 paddingstring 类型,因此可以安全地执行相应的操作。

更复杂的类型守卫

自定义类型守卫不仅可以用于基本类型,还可以用于更复杂的情况,如接口或类实例:

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 函数接受一个可能是 BirdFish 的参数,并检查其是否具有 fly 方法。如果条件成立,则返回 true 并告知 TypeScript 该值是一个 Bird 类型;否则返回 false

组合多个类型守卫

你还可以组合多个类型守卫来处理更复杂的逻辑:

typescript
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 编译器知道在这个分支中 shapeCircle 类型,因此可以安全地访问 radius 属性。

注意事项

  • 类型谓词格式:确保你的类型守卫函数返回的是正确的类型谓词格式,即 parameterName is Type

  • 类型兼容性:类型守卫中的类型必须与联合类型中的某个候选类型兼容。例如,如果你有一个 string | number 的联合类型,那么类型守卫应该返回 value is stringvalue is number

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

总结

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

Released under the MIT License.