TypeScript-3-7-断言函数

date
Dec 1, 2019
slug
TypeScript-3-7-断言函数
status
Published
tags
JavaScript
TypeScript
summary
type
Post

断言函数

有一类特定的函数,在非预期结果出现时会抛出一个错误。 这类函数就叫做断言函数。 例如,Node.js 有一个专用的断言函数叫 assert
assert(someValue === 42);
在这个示例中,如果 someValue 不等于 42,那么 assert 就会抛出一个 AssertionError
JavaScript 中的断言经常用于确保传入的是正确的类型。 比如,
function multiply(x, y) {
  assert(typeof x === 'number');
  assert(typeof y === 'number');

  return x * y;
}
不幸的是,TypeScript 中的这些检查从来不会被正确的编写。 对于弱类型的写法,意味着 TypeScript 不会严格检查,而对于稍微规范一些的写法,一般要求使用者添加类型断言。
function yell(str) {
  assert(typeof str === 'string');

  return str.toUppercase();
  // 哎呀!我们错误地拼写了 'toUpperCase'
  // 如果 TypeScript 依然能捕获问题,那就太棒了!
}
这里有可供选择的替代写法,可以让编程语言分析出问题,不过并不方便。
function yell(str) {
  if (typeof str !== 'string') {
    throw new TypeError('str should have been a string.');
  }
  // 错误被捕获!
  return str.toUppercase();
}
TypeScript 最基本的目标就是用最友好的方式去对现有的 JavaScript 结构做类型分析。 基于这个原因,TypeScript 3.7 引入了一个新的概念叫“断言签名(assertion signatures)”,用来模拟这些断言函数。
第一种断言签名,模拟 Node 中的 assert 函数的功能。 它确保在断言的范围内,断言条件必须为 true
function assert(condition: any, msg?: string): asserts condition {
    if (!condition) {
        throw new AssertionError(msg)
    }
}
断言条件 说的是,如果 assert 返回了,condition 参数得到的传入值必须为 true,因为如果不是这样,它肯定会抛出一个错误。 这意味着,在断言的范围内,这个条件一定是正确的。 举一个例子,用这个断言函数意味着我们可以实现捕获我们之前的 yell 示例的错误。
function yell(str) {
    assert(typeof str === "string");

    return str.toUppercase();
    //         ~~~~~~~~~~~
    // error: Property 'toUppercase' does not exist on type 'string'.
    //        Did you mean 'toUpperCase'?
}

function assert(condition: any, msg?: string): asserts condition {
    if (!condition) {
        throw new AssertionError(msg)
    }
}
另外一种断言签名不是用来校验一个条件,而是告诉 TypeScript 某个变量或属性有不同的类型。
function assertIsString(val: any): asserts val is string {
    if (typeof val !== "string") {
        throw new AssertionError("Not a string!");
    }
}
这里 asserts val is string 保证在 assertIsString 调用之后, 任何传入的变量将被认为是一个 string.
function yell(str: any) {
  assertIsString(str);

  // 现在 TypeScript 知道了,'str' 是一个 'string'。

  return str.toUppercase();
  //         ~~~~~~~~~~~
  // error: Property 'toUppercase' does not exist on type 'string'.
  //        Did you mean 'toUpperCase'?
}
这里的断言签名非常类似于类型断言签名:
function isString(val: any): val is string {
  return typeof val === 'string';
}

function yell(str: any) {
  if (isString(str)) {
    return str.toUppercase();
  }
  throw 'Oops!';
}
就像类型断言签名一样,这些断言签名是非常神奇的。 我们可以用它们实现一些非常复杂的想法和设计。
function assertIsDefined<T>(val: T): asserts val is NonNullable<T> {
    if (val === undefined || val === null) {
        throw new AssertionError(
            `Expected 'val' to be defined, but received ${val}`
        );
    }
}
想阅读更多断言签名相关内容, 签出原始的 pull request.

© XieZhichao 2022 - 2024