TypeScript 5.0於2023年3月16日正式釋出,現在可以供大家使用了。這個版本引入了許多新功能,目的是使TypeScript更小、更簡單、更快速。
這個新版本對用於類定製的裝飾器進行了現代化處理,允許以可重複使用的方式定製類和其成員。開發人員現在可以在型別引數宣告中新增一個const修飾符,允許類似const的推斷成為預設的。新版本還使所有列舉成為union列舉,簡化了程式碼結構,加快了TypeScript的體驗。
在這篇文章中,你將探索TypeScript 5.0中引入的變化,深入瞭解其新特性和功能。
開始使用TypeScript 5.0
TypeScript 是一個官方編譯器,你可以使用 npm 安裝到你的專案中。如果你想在你的專案中開始使用TypeScript 5.0,你可以在你的專案目錄中執行以下命令:
npm install -D typescript
這將在node_modules目錄下安裝編譯器,現在你可以用 npx tsc
命令執行它。
你還可以在這個文件中找到關於在Visual Studio Code中使用較新版本TypeScript的說明。
TypeScript 5.0 有哪些新特性?
在這篇文章中,讓我們來探討TypeScript中引入的5個主要更新。這些功能包括:
現代化的裝飾器
裝飾器在TypeScript中已經存在了一段時間,但新版本使其與ECMAScript的提議保持一致,現在處於第三階段,這意味著它處於被新增到TypeScript的階段。
裝飾器是一種以可重複使用的方式定製類和其成員的行為的方法。例如,如果你有一個類,它有兩個方法, greet
和 getAge
:
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}.`); } getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
在現實世界的用例中,這個類應該有更復雜的方法來處理一些非同步邏輯,並有副作用,例如,你會想扔進一些 console.log
呼叫來幫助除錯方法。
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log('LOG: Method Execution Starts.'); console.log(`Hello, my name is ${this.name}.`); console.log('LOG: Method Execution Ends.'); } getAge() { console.log('LOG: Method Execution Starts.'); console.log(`I am ${this.age} years old.`); console.log('Method Execution Ends.'); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
這是一個經常出現的模式,如果有一個適用於每一個方法的解決方案,那就很方便了。
這就是裝飾器發揮作用的地方。我們可以定義一個名為 debugMethod
的函式,如下所示:
function debugMethod(originalMethod: any, context: any) { function replacementMethod(this: any, ...args: any[]) { console.log('Method Execution Starts.'); const result = originalMethod.call(this, ...args); console.log('Method Execution Ends.'); return result; } return replacementMethod; }
在上面的程式碼中, debugMethod
接收原始方法( originalMethod
),並返回一個函式,做以下工作:
- 記錄 “Method Execution Starts.” 的資訊。
- 傳遞原始方法和它的所有引數(包括這個)。
- 記錄一條訊息 “Method Execution Ends.”。
- 返回原始方法返回的東西。
通過使用裝飾器,你可以將 debugMethod
應用到你的方法中,如下面的程式碼所示:
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } @debugMethod greet() { console.log(`Hello, my name is ${this.name}.`); } @debugMethod getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
這將輸出以下內容:
LOG: Entering method. Hello, my name is Ron. LOG: Exiting method. LOG: Entering method. I am 30 years old. LOG: Exiting method.
在定義裝飾函式( debugMethod
)時,會傳遞第二個引數,叫做 context
(它是context物件–有一些關於裝飾方法如何被宣告的有用資訊,也有方法的名稱)。你可以更新你的 debugMethod
,從 context
物件中獲取方法的名稱:
function debugMethod( originalMethod: any, context: ClassMethodDecoratorContext ) { const methodName = String(context.name); function replacementMethod(this: any, ...args: any[]) { console.log(`'${methodName}' Execution Starts.`); const result = originalMethod.call(this, ...args); console.log(`'${methodName}' Execution Ends.`); return result; } return replacementMethod; }
當你執行你的程式碼時,現在輸出將帶有每個用 debugMethod
裝飾器裝飾的方法的名字:
'greet' Execution Starts. Hello, my name is Ron. 'greet' Execution Ends. 'getAge' Execution Starts. I am 30 years old. 'getAge' Execution Ends.
你可以用裝飾器做的事情還有很多。請隨時檢視原始拉取請求,以獲得更多關於如何在TypeScript中使用裝飾器的資訊。
引入const型別引數
這是另一個重要的版本,它為你提供了一個新的泛型工具,以改善你在呼叫函式時得到的推斷。預設情況下,當你用 const
宣告值時,TypeScript會推斷出型別而不是其字面值:
// Inferred type: string[] const names = ['John', 'Jake', 'Jack'];
直到現在,為了實現所需的推理,你不得不通過新增 “as const “來使用const斷言:
// Inferred type: readonly ["John", "Jake", "Jack"] const names = ['John', 'Jake', 'Jack'] as const;
當你呼叫函式時,情況類似。在下面的程式碼中,推斷出的countries的型別是 string[]
:
type HasCountries = { countries: readonly string[] }; function getCountriesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: string[] const countries = getCountriesExactly({ countries: ['USA', 'Canada', 'India'] });
你可能希望有一個更具體的型別,在這之前,有一種方法可以解決這個問題,那就是新增 as const
斷言:
// Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] } as const);
這可能很難記住和實現。然而,TypeScript 5.0引入了一個新的功能,你可以在型別引數宣告中新增一個const修飾符,這將自動應用一個類似const的預設推理。
type HasCountries = { countries: readonly string[] }; function getNamesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] });
使用 const
型別引數可以讓開發者在他們的程式碼中更清楚地表達意圖。如果一個變數打算成為常量,並且永遠不會改變,那麼使用 const
型別引數可以確保它永遠不會被意外地改變。
你可以檢視原始拉取請求,瞭解更多關於const型別引數在TypeScript中的工作原理。
對列舉的改進
TypeScript 中的列舉是一個強大的結構,允許開發者定義一組命名的常量。在TypeScript 5.0中,對列舉進行了改進,使其更加靈活和有用。
例如,如果你有以下列舉傳入一個函式:
enum Color { Red, Green, Blue, } function getColorName(colorLevel: Color) { return colorLevel; } console.log(getColorName(1));
在引入TypeScript 5.0之前,你可以傳遞一個錯誤的級別號,而且它不會丟擲錯誤。但隨著TypeScript 5.0的引入,它將立即丟擲一個錯誤。
此外,新版本通過為每個計算成員建立一個獨特的型別,使所有列舉成為聯合列舉。這一改進允許縮小所有列舉的範圍,並將其成員作為型別進行引用:
enum Color { Red, Purple, Orange, Green, Blue, Black, White, } type PrimaryColor = Color.Red | Color.Green | Color.Blue; function isPrimaryColor(c: Color): c is PrimaryColor { return c === Color.Red || c === Color.Green || c === Color.Blue; } console.log(isPrimaryColor(Color.White)); // Outputs: false console.log(isPrimaryColor(Color.Red)); // Outputs: true
TypeScript 5.0的效能改進
TypeScript 5.0 包括程式碼結構、資料結構和演算法擴充套件方面的眾多重大變化。這有助於改善整個 TypeScript 體驗,從安裝到執行,使其更快、更高效。
例如,TypeScript 5.0和4.9的包大小之間的差異是相當驚人的。
TypeScript最近從名稱空間遷移到了模組,使其能夠利用現代構建工具,可以進行範圍提升等優化。此外,刪除了一些廢棄的程式碼,從TypeScript 4.9的63.8 MB包大小中減少了約26.4 MB。
TypeScript包的大小
下面是TypeScript 5.0和4.9之間在速度和大小上的一些更有趣的對比:
場景 | 相對於TS 4.9的時間或大小 |
material-ui構建時間 | 90% |
TypeScript編譯器的啟動時間 | 89% |
Playwright構建時間 | 88% |
TypeScript Compiler自建時間 | 87% |
Outlook Web構建時間 | 82% |
VS Code構建時間 | 80% |
typescript npm包大小 | 59% |
捆綁器解決更好的模組解析
當你在TypeScript中寫一個匯入語句時,編譯器需要知道這個匯入指的是什麼。它使用模組解析來實現這一點。例如,當你寫 import { a } from "moduleA"
時,編譯器需要知道 moduleA
中 a
的定義以檢查其使用。
在TypeScript 4.7中,為 --module
和 moduleResolution
設定增加了兩個新選項: node16
和 nodenext
。
這些選項的目的是為了更準確地表示Node.js中ECMAScript模組的精確查詢規則。然而,這種模式有幾個限制,是其他工具所不能執行的。
例如,在Node.js中的ECMAScript模組中,任何相對匯入都必須包括一個副檔名才能正確工作:
import * as utils from "./utils"; // Wrong
import * as utils from "./utils.mjs"; // Correct
TypeScript引入了一個新的策略,叫做 “moduleResolution bundler”。這個策略可以通過在你的TypeScript配置檔案的 “compilerOptions “部分新增以下程式碼來實現:
{
"compilerOptions": {
"target": "esnext",
"moduleResolution": "bundler"
}
}
這個新策略適用於那些使用現代捆綁器的人,如Vite、esbuild、swc、Webpack、Parcel和其他利用混合查詢策略的捆綁器。
你可以檢視原始拉取請求及其實現,以瞭解更多關於 moduleResolution
捆綁器在TypeScript中如何工作的資訊。
變動和棄用
TypeScript 5.0新增了部分變動和棄用,包括執行時間要求、lib.d.ts 更改和 API 突破性更改。
- 執行時要求: TypeScript現在以ECMAScript 2018為目標,該包設定的最低引擎期望值為12.20。因此,Node.js的使用者應該至少有12.20或更高的版本來使用TypeScript 5.0。
- lib.d.ts的變化: 對於如何生成DOM的型別有一些變化,這可能會影響到現有的程式碼。特別是,某些屬性已經從數字轉換為數字字面型別,用於剪下、複製和貼上事件處理的屬性和方法已經跨介面移動。
- API的突破性變化: 一些不必要的介面已被刪除,並進行了一些正確性的改進。TypeScript 5.0也已經轉移到了模組。
TypeScript 5.0已經廢棄了某些設定和它們相應的值,包括目標: target: ES3
, out
, noImplicitUseStrict
, keyofStringsOnly
, suppressExcessPropertyErrors
, suppressImplicitAnyIndexErrors
, noStrictGenericChecks
, charset
, importsNotUsedAsValues
, 和 preserveValueImports
,以及專案引用中的prepend。
雖然這些配置在TypeScript 5.5之前仍然有效,但我們會發出警告,提醒仍在使用這些配置的使用者。
小結
在這篇文章中,你已經瞭解了TypeScript 5.0帶來的一些主要功能和改進,比如對列舉、捆綁器解析和常量型別引數的改進,以及對速度和大小的改進。
評論留言