TypeScript:类型谓词介绍指南

940 阅读3分钟

首页文章

TypeScript中的类型谓词可以帮助你根据条件来缩小你的类型。它们类似于类型守卫,但在函数上工作。它们的工作方式是,如果一个函数返回真,就把参数的类型改成更有用的东西。

让我们从一个基本的例子开始。假设你有一个检查某个值是否为字符串类型的函数。

function isString(s) {
  return typeof s === 'string';
}

在另一个函数中使用isString 函数。

function toUpperCase(x: unknown) {
  if(isString(x)) {
    x.toUpperCase(); // ⚡️ x is still of type unknown
  }
}

TypeScript会抛出一个错误。我们可以确定x ,在这一点上是字符串类型。但由于验证被包裹在一个函数中,x 的类型不会改变(与类型守护相反)。进入类型谓词。

让我们明确地告诉TypeScript,如果isString 评估为真,参数的类型就是字符串。

function isString(s): s is string {
  return typeof s === 'string';
}

TypeScript现在知道我们在我们的toUpperCase 函数中处理的是字符串。

function toUpperCase(x: unknown) {
  if(isString(x)) {
    x.toUpperCase(); // ✅ all good, x is string
  }
}

缩小集合的范围#

这不仅可以帮助你处理未知的类型,或多个类型,还可以缩小一个类型中的集合。让我们有一个程序,你扔一个骰子。每当你扔出一个六,你就赢了。

function pipsAreValid(pips: number) {
  // we check for every discrete value, as number can 
  // be something between 1 and 2 as well.
  return pips === 1 || pips === 2 || pips === 3 ||
    pips === 4 || pips === 5 || pips === 6;
}

function evalThrow(count: number) {
  if (pipsAreValid(count)) {
    // my types are lying 😢
    switch (count) {
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
        console.log('Not today');
        break;
      case 6:
        console.log('Won!');
        break;
      case 7:
        // TypeScript does not complain here, even though
        // it's impossible for count to be 7
        console.log('This does not work!');
        break;
    }
  }
}

这个程序起初看起来不错,但从类型的角度看有一些问题:count 是数字类型。这作为一个输入参数是可以的。我们马上验证count 是一个1到6之间的数字。一旦我们验证了这一点,count 就不再是任何数字了。它被缩小到一个离散的六个值的集合。

因此,从switch语句开始,我的类型就在说谎了为了防止进一步的复杂化,让我们使用联合类型将数字集缩小到这六个离散值。

type Dice = 1 | 2 | 3 | 4 | 5 | 6;

function pipsAreValid(pips: number): pips is Dice {
  return pips === 1 || pips === 2 || pips === 3 ||
    pips === 4 || pips === 5 || pips === 6;
}

function evalThrow(count: number) {
  if (pipsAreValid(count)) {
    // count is now of type Dice 😎
    switch (count) {
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
        console.log('Not today');
        break;
      case 6:
        console.log('Won!');
        break;
      case 7:
        // TypeScript errors here. 7 is not in the union type of 
        // Dice
        console.log('This does not work!');
        break;
    }
  }
}

对我们和我们的同事来说,类型安全了很多。当然,这种 "类型转换 "可以是任何有意义的东西,以加强你的应用程序。即使你验证了复杂的对象,你也可以将你的参数缩小到一个特定的类型,并确保它们与你代码的其他部分相处。很有用,特别是当你依赖大量的函数时。