This website requires JavaScript.

TypeScript -- 断言函数

2019.11.30 11:33 字数 3158 阅读 177 喜欢 2 评论 0

此章节节选自 TypeScript 3.7 ,更多 TypeScript 3.7 内容,请 点击查看

有一类特定的函数,在非预期结果出现时会抛出一个错误。这类函数就叫做断言函数。例如,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();
  // Oops! We misspelled 'toUpperCase'.
  // Would be great if TypeScript still caught this!
}

这里有可供选择的替代写法,可以让 TypeScript 分析出问题,不过并不方便。

function yell(str) {
  if (typeof str !== 'string') {
    throw new TypeError('str should have been a string.');
  }
  // Error caught!
  return str.toUppercase();
}

TypeScript 最基本的目标就是用最友好的方式键入现有的 JavaScript 结构。基于这个原因,TypeScript 3.7 引入了一个新的概念叫「断言签名(assertion signatures)」,用来模拟这些断言函数。

第一种断言签名,模拟 Node 中的 assert 函数的功能。它确保在断言的范围内,断言条件必须为这个真。

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

asserts condition 的意思是,如果 assert 函数有返回,传入 condition 的参数必须为真,因为如果不是这样,它肯定会抛出一个错误。这意味着,在剩下的作用域中(if 条件后)condition 必须为 truthy

举一个例子,用这个断言函数意味着我们可以实现捕获之前的 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);

  // Now TypeScript knows that 'str' is a 'string'.

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

这里的断言签名非常类似于类型谓词(predicate)签名:

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 .

赞赏支持

微信

支付宝

相关推荐

暂无推荐文章