# 类型保护

联合类型看起来很不错,但使用起来并非如此,你必须要进行类型断言才能进一步的操作。也可以说是一步一步的缩小联合类型的范围来进行类型保护。

# typeof 类型保护

function setConfig(cnf: string | number) {
  if (typeof cnf === "string") {
    // TODO 如果是字符串,进行相关操作
  } else {
    // TODO 如果是数字, 进行相关操作
  }
}

# 真实性

挺抽象,因为 0 , "" , NAN , null , undefined 都可以代表布偶值。

function doSomething(params: IParam | number | null) {
  // 在 ts 里仍然可以这样表示 boolean
  if (params) {
    // 已经排除了 null , IParam | number
    if (typeof params === "number") {
      // 推断出 params 的类型时 number
    } else {
      // 类型只剩下 IParam
      params.say();
    }
  }
}

# 使用相等来缩小范围

function dosomething(x: string | number, y: number | boolean) {
  if (x === y) {
    return (x + y).toFixed(2);
  }
  // TODO 接着搞事情
}

如果 xy 相等,表示 xy 都是 number 类型

# 使用 in 来缩小范围

JavaScript 中我们经常使用 in 来判断对象中是否包含某个属性,那么在这里同样也可以判断类型。

示例:

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim();
  }

  return animal.fly();
}

"swim" in animal 表示了 animalFish 类型

# 使用 instanceof 来缩小范围

instanceof 表示一个对象是否是一个构造函数的实例,比如判断一个对象是否数组: a instanceof Array

通过 instanceof 来缩小联合范围:

function logValue(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());
  } else {
    // 剩下的就只有 string 了
    console.log(x.toUpperCase());
  }
}

通过判断 xDate 的实例来缩小范围

# 使用类型谓词

还是那个例子,我们增加一个方法,假设 pet 就是 fish,然后返回判断条件

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

再来看下使用过程

let pet = getSmallPet();

if (isFish(pet)) {
  pet.swim();
} else {
  pet.fly();
}

还有意外收获

const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()];
const underWater1: Fish[] = zoo.filter(isFish);
// or, equivalently
const underWater2: Fish[] = zoo.filter(isFish) as Fish[];

// The predicate may need repeating for more complex examples
const underWater3: Fish[] = zoo.filter((pet): pet is Fish => {
  if (pet.name === "sharkey") return false;
  return isFish(pet);
});

可以直接过滤, 还能自定义过滤

# 可识别联合

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

可以根据 kind 直接识别出类型

function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius ** 2;
  }
}