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 谓词都能触发类型收窄;灵活组合它们,联合类型在分支内既安全又好用,运行时判断与编译期类型两不误。