TypeScript 类型守卫

联合类型变量的具体类型往往要到运行期才能确定,直接访问成员会报错。类型守卫是一系列能在分支内自动收窄类型的检查方式,让 TypeScript 在 if 里「看清」当前到底是哪一种类型,从而安全调用对应成员。

为什么需要类型守卫

没有守卫时编译器只认联合类型,无法确定成员方法是否可用;用 typeof 收窄后即可安全调用:

function printId(id: string | number) {
  // console.log(id.toUpperCase()); // 报错:number 没有 toUpperCase
  if (typeof id === "string") {
    console.log(id.toUpperCase()); // 收窄为 string 后安全
  }
}
printId("abc"); // 输出:ABC

typeof 守卫:原始类型

typeof 适合区分 string、number、boolean 等原始类型,else 分支自动收窄为剩余类型:

function show(v: string | number) {
  if (typeof v === "string") {
    console.log(v.toUpperCase()); // v 收窄为 string
  } else {
    console.log(v.toFixed(2));    // v 收窄为 number
  }
}
show("hello"); // 输出:HELLO
show(3.1415);  // 输出:3.14

instanceof 守卫:类实例

instanceof 按构造函数区分对象,常用于类层次结构:

class Dog {
  bark(): void { console.log("汪汪"); }
}
class Cat {
  meow(): void { console.log("喵喵"); }
}
function play(animal: Dog | Cat) {
  if (animal instanceof Dog) animal.bark(); // 收窄为 Dog
  else animal.meow(); // 收窄为 Cat
}
play(new Dog()); // 输出:汪汪

in 守卫:按成员存在判断

in 检查属性是否存在于对象上,适合区分结构不同的类型:

type Fish = { swim(): void };
type Bird = { fly(): void };
function move(animal: Fish | Bird) {
  if ("swim" in animal) animal.swim(); // 有 swim 成员 → Fish
  else animal.fly(); // 否则是 Bird
}

自定义类型守卫

内置判断不够表达时,可写返回 参数 is 类型(类型谓词)的函数,把判断逻辑封装复用:

interface Square { kind: "square"; size: number }
interface Circle { kind: "circle"; radius: number }
type Shape = Square | Circle;
function isSquare(s: Shape): s is Square {
  return s.kind === "square"; // 返回 true 即告诉编译器 s 是 Square
}
function area(s: Shape): number {
  return isSquare(s) ? s.size * s.size : Math.PI * s.radius * s.radius;
}
console.log(area({ kind: "circle", radius: 1 })); // 输出:3.141592653589793

可辨识联合的自动收窄

联合成员带共有判别字段时,if/switch 之后类型会自动收窄,是处理复杂状态最常用的一招:

type Res = { ok: true; data: string } | { ok: false; err: string };
function handle(r: Res) {
  if (r.ok) {
    console.log("数据:" + r.data); // 收窄为成功分支
  } else {
    console.log("错误:" + r.err);
  }
}
handle({ ok: true, data: "内容" }); // 输出:数据:内容

小结:typeof、instanceof、in 与自定义 is 谓词都能触发类型收窄;灵活组合它们,联合类型在分支内既安全又好用,运行时判断与编译期类型两不误。

笔记加载中…