断言守卫

断言守卫和类型守卫最大的不同点在于,在判断条件不通过时,断言守卫需要抛出一个错误,类型守卫只需要剔除掉预期的类型。这里的抛出错误可能让你想到了 never 类型,但实际情况要更复杂一些,断言守卫并不会始终都抛出错误,所以它的返回值类型并不能简单地使用 never 类型。

function assert(condition: any, msg?: string): asserts condition {
  if (!condition) {
    throw new Error(msg);
  }
}

这里使用的是 asserts condition,而 condition 来自于实际逻辑!这也意味着,我们将 condition 这一逻辑层面的代码,作为了类型层面的判断依据,相当于在返回值类型中使用一个逻辑表达式进行了类型标注

举例来说,对于 assert(typeof name === 'number'); 这么一个断言,如果函数成功返回,就说明其后续的代码中 condition 均成立,也就是 name 神奇地变成了一个 number 类型。

这里的 condition 甚至还可以结合使用 is 关键字来提供进一步的类型守卫能力。

let name: any = 'aaa';

function assertIsNumber(val: any): asserts val is number {
  if (typeof val !== 'number') {
    throw new Error('Not a number!');
  }
}

assertIsNumber(name);

// name 为 number 类型
name.toFixed();