此章节节选自 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 .