TypeScript 3.7 發布了,此版本帶來了許多新特性。
Optional Chaining
首先一大亮點是 Optional Chaining,這是社區呼喚特彆強烈的一個 ECMAScript 特性。最初它是 TypeScript 的 issue 跟蹤器上第 16 個 issue,於 5 年前提交,要知道目前總共有多達 23000 個 issue。當時 TC39 還沒有正式提案,這些年來開發者一直要求實現該特性,但是為了不與 ECMAScript 建議衝突,遲遲沒有實現,相反 TS 開發團隊一直在幫助推動該提案實現標準化,並最終推及所有 JavaScript 與 TypeScript 開發者。
Optional Chaining 特性主要用於保護出現在屬性路徑中 null 和 undefined 值,像 C# 等語言中已經有用於訪問屬性鏈的語法糖,可以在對象層次結構中的任何地方處理遇到的 null 和 undefined 情況,使它可以正常執行,而不會拋出錯誤。
具體來講,在向樹狀結構深處進行屬性值訪問時,通常需要檢查中間節點是否存在:
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) // 自判斷函數或方法調用
null 判斷合併(Nullish Coalescing)
另一個與 Optional Chaining 很接近的特性是 null 判斷合併(Nullish Coalescing),由特定的 Nullish Coalescing 操作符 ?? 啟用,它也是即將到來的 ECMAScript 特性。
考慮以下代碼:
let x = (foo !== null && foo !== undefined) ?
foo :
bar();
如果 foo 不為空並且不等於 undefined,則執行 bar()。它現在可以等效於:
let x = foo ?? bar();
斷言
如果發生意外情況,則有一組特定函數會拋出錯誤,這被稱為斷言。例如 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 中,會有一些類型問題:
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();
}
TypeScript 希望以最小的破壞性方式保留現有 JavaScript 結構,因此 TypeScript 3.7 引入了一個稱為「斷言簽名」的新概念,可以對這些斷言函數進行建模。
第一種斷言簽名對 Node.js assert 的執行方式進行建模,它確保在包含範圍的其餘部分中,無論檢查什麼條件都必須為真。
function assert(condition: any, msg?: string): asserts condition {
if (!condition) {
throw new AssertionError(msg)
}
}
assert condition 表示,如果 assert 返回,則傳遞給 condition 參數的任何內容都必須為 true(否則會引發錯誤),這意味著對於其餘範圍,該條件必須是 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!");
}
}
這裡的 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
另外,作為更新的一部分,TypeScript 官網還有一個遊樂場(Playground)可以體驗所有新特性:
https://www.typescriptlang.org/play/index.html?#show-whatisnew
[admin
]