跳到主要内容

类型守卫

联合类型为什么需要类型守卫?​

  • 限制: 联合类型只能访问所有成员共有的属性与方法, 见 联合类型与交叉类型;
  • 目标: 在分支中把联合类型收窄为其中一个成员, 从而访问其专有成员;
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}

declare function getSmallPet(): Fish | Bird;

const pet = getSmallPet();
pet.layEggs(); // 通过, layEggs 是共有成员
pet.swim(); // 报错, 编译器无法确定 pet 是 Fish

如何用 is 定义类型判定函数?​

  • 语法: 返回值写成 parameter is 类型;
  • 效果: 判定通过的分支里, 参数类型被收窄为目标类型;
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}

if (isFish(pet)) {
pet.swim(); // pet: Fish
} else {
pet.fly(); // pet: Bird
}

in 操作符如何收窄类型?​

  • 语法: "属性名" in 值;
  • 效果: 编译器按是否存在该属性收窄类型;
function move(pet: Fish | Bird) {
if ("swim" in pet) {
return pet.swim();
}
return pet.fly();
}

typeof 与 instanceof 如何自动收窄类型?​

  • 自动守卫: 编译器把 typeof 与 instanceof 判断直接作为类型守卫;
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
declare const padder: SpaceRepeatingPadder | StringPadder;

if (padder instanceof SpaceRepeatingPadder) {
padder; // 收窄为 SpaceRepeatingPadder
}
if (padder instanceof StringPadder) {
padder; // 收窄为 StringPadder
}
  • 边界: typeof 只覆盖 undefined、boolean、number、string、symbol、function、object, 其余引用类型要用 instanceof 或自定义守卫;