TypeScript 3.7 发布了,此版本带来了许多新特性。
具体来讲,在向树状结构深处进行属性值访问时,通常需要检查中间节点是否存在:
var street = user.address && user.address.street;
许多 API 返回一个对象或 null/undefined,并且可能只想在结果不为 null 时从结果中提取属性:
var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined
Optional Chaining 运算符允许开发人员直接用简单的方式处理这种情况,而不用进行重复性操作,或者使用临时变量分配中间结果:
var street = user.address?.street
var fooValue = myForm.querySelector('input[name=foo]')?.value
因为是保护访问属性链时的 null 与 undefined,所以 Optional Chaining 运算符也叫做“安全导航运算符”,TC39 标准中给出的该运算符是“?.”,它的语法可以适用于三种场景:
obj?.prop // 自判断静态属性访问
obj?.[expr] // 自判断动态访问
func?.(...args) // 自判断函数或方法调用
考虑以下代码:
let x = (foo !== null && foo !== undefined) ?
foo :
bar();
如果 foo 不为空并且不等于 undefined,则执行 bar()。它现在可以等效于:
let x = foo ?? bar();
如果发生意外情况,则有一组特定函数会抛出错误,这被称为断言。例如 Node.js 有一个专用的 assert 函数:
assert(someValue === 42);
JavaScript 中的断言通常用于防止传入不正确的类型,例如:
function multiply(x, y) {
assert(typeof x === "number");
assert(typeof y === "number");
return x * y;
}
不过在 TypeScript 中,会有一些类型问题:
function yell(str) {
assert(typeof str === "string");
return str.toUppercase();
// Oops! We misspelled 'toUpperCase'.
// Would be great if TypeScript still caught this!
}
替代方法是改写代码,以便语言可以对其进行分析,但这不太方便:
function yell(str) {
if (typeof str !== "string") {
throw new TypeError("str should have been a string.")
}
// Error caught!
return str.toUppercase();
}
第一种断言签名对 Node.js assert 的执行方式进行建模,它确保在包含范围的其余部分中,无论检查什么条件都必须为真。
function assert(condition: any, msg?: string): asserts condition {
if (!condition) {
throw new AssertionError(msg)
}
}
以前边的 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!");
}
}
这里的 assert val is string,确保在对 assertIsString 进行任何调用之后,传入的任何变量都将是字符串。
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'?
}
这些断言签名与编写类型谓词签名非常相似:
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}`
);
}
}
此外还有其它新特性与特性增强,详情查看更新说明:
https://devblogs.microsoft.com/typescript/announcing-typescript-3-7
https://www.typescriptlang.org/play/index.html?#show-whatisnew