TypeScript 中的类型守卫是什么

当我们有特定于特定类型的逻辑时,我们会在 typescript 中使用类型保护。

用代码说话:

if (typeof someValue === 'string') {
  // someValue 保证是字符串类型
   // => 因此我们可以使用它执行所有字符串操作
   // 比如调用 someValue.toLowerCase() 等
}

我们希望强制 typescript 允许我们使用一个值,就好像它保证是“x”类型一样。 一旦我们运行了一个类型保护,我们就明确了我们正在使用的值的类型。

function sum(a: string | number, b: string | number): string | number {
  // a 和 b 是字符串或数字类型
  // 因此我们只能对它们使用方法,
  // 字符串和数字之间的共同点
  // 即 .toString() 和 .valueOf()
  if (typeof a === 'string' && typeof b === 'string') {
    // a 和 b 都保证是 string 类型可以调用它们的所有字符串方法
    return a.toUpperCase() + b.toUpperCase();
  }

  if (typeof a === 'number' && typeof b === 'number') {
    // a 和 b 都保证是 number 类型可以调用它们的所有 number 方法
    return a.toFixed(2) + b.toFixed(2);
  }

  if (typeof a === 'string' || typeof b === 'string') {
    // 我们不知道 a 还是 b 是字符串,所以我们不能在其中任何一个上调用字符串方法
    return String(a) + String(b);
  }

  // 我们已经用尽了所有的可能性,现在 a 和 b 保证是“数字”类型
  return a.toFixed(2) + b.toFixed(2);
}

当我们使用联合类型时,我们经常使用类型保护,如果我们有一个非此即彼的场景,我们必须缩小我们正在使用的值的类型,以确保我们尝试执行的操作是类型安全的。

检查原语与检查对象

在上面的示例中,我们使用 typeof 来缩小类型并使用类型保护,我们只使用带有原始值的 typeof

  • if (typeof someNumber === ‘number’) {保证为数字}
  • if (typeof someString === ‘string’) {保证是字符串}
  • if (typeof someBoolean === ‘boolean’) {保证是布尔类型}

对于非原始类型,我们使用 instanceof

  • if (someObject instanceof someClass) {保证是 someClass 类型}
function loop(collection: string[] | string): void {
  // 只能访问两者的常用方法 string[] 和 string
  if (Array.isArray(collection)) {
    // 集合保证是一个 string[]
    collection.push('hello world');
  }

  // 上面是一样的
  if (collection instanceof Array) {
    // 集合保证是一个字 string[]
    collection.push('hello world');
  }

  if (typeof collection === 'string') {
    // 集合保证是一个 string
    console.log(collection.toUpperCase());
  }
}

即使 typeof 可以与对象一起使用,它也不能作为 typescript 中的类型保护。 例如,javascript 中的 typeof 数组将返回“object”,但它在 typescript 中不能用作类型保护。

每当我们想要缩小联合类型的类型并在值上恢复一组特定于类型的属性时,我们都会使用类型保护。