TypeScript 與 JavaScript 的關係不同尋常。TypeScript 提供了 JavaScript 的所有功能,並在其之上增加了一層:TypeScript 的型別系統。
例如,JavaScript 提供了諸如 string 和 number 這樣的語言原語,但它不會檢查你是否始終如一地分配了這些型別。而 TypeScript 會。
這意味著你現有的、可執行的 JavaScript 程式碼同樣也是 TypeScript 程式碼。TypeScript 的主要優勢在於它能夠突出顯示程式碼中意外的行為,從而降低出現 Bug 的機率。
本教程簡要概述了 TypeScript,重點介紹其型別系統。
型別推論
TypeScript 瞭解 JavaScript 語言,並在許多情況下為你生成型別。例如,在建立變數並將其賦值為特定值時,TypeScript 會使用該值作為其型別。
tsTrylethelloWorld = "Hello World";
透過理解 JavaScript 的工作方式,TypeScript 可以構建一個既接受 JavaScript 程式碼又具備型別的型別系統。這使得你無需新增額外的字元來顯式定義型別。這就是為什麼在上面的例子中,TypeScript 知道 helloWorld 是一個 string。
你可能在 Visual Studio Code 中編寫過 JavaScript,並體驗過編輯器自動補全功能。Visual Studio Code 在底層使用 TypeScript 來使 JavaScript 開發變得更加輕鬆。
定義型別
你可以在 JavaScript 中使用多種設計模式。然而,有些設計模式使得型別很難自動推斷(例如使用動態程式設計的模式)。為了涵蓋這些情況,TypeScript 支援對 JavaScript 語言進行擴充套件,為你提供告知 TypeScript 型別應該是何種形式的位置。
例如,若要建立一個包含 name: string 和 id: number 的推斷型別物件,你可以這樣寫:
tsTryconstuser = {name : "Hayes",id : 0,};
你可以使用 interface(介面)宣告顯式描述該物件的形狀:
tsTryinterfaceUser {name : string;id : number;}
隨後,你可以在變數聲明後使用 : TypeName 語法,宣告一個 JavaScript 物件符合你新定義的 interface 的形狀。
tsTryconstuser :User = {name : "Hayes",id : 0,};
如果你提供的物件與你指定的介面不匹配,TypeScript 會向你發出警告。
tsTryinterfaceUser {name : string;id : number;}constuser :User = {Object literal may only specify known properties, and 'username' does not exist in type 'User'.2353Object literal may only specify known properties, and 'username' does not exist in type 'User'.: "Hayes", username id : 0,};
由於 JavaScript 支援類和麵向物件程式設計,TypeScript 也同樣支援。你可以將介面宣告與類一起使用。
tsTryinterfaceUser {name : string;id : number;}classUserAccount {name : string;id : number;constructor(name : string,id : number) {this.name =name ;this.id =id ;}}constuser :User = newUserAccount ("Murphy", 1);
你可以使用介面來註解函式的引數和返回值。
tsTryfunctiondeleteUser (user :User ) {// ...}functiongetAdminUser ():User {//...}
JavaScript 中已有一組可用的原始型別:boolean、bigint、null、number、string、symbol 和 undefined,你可以在介面中使用它們。TypeScript 在此基礎上擴充套件了一些型別,例如 any(允許任何內容)、unknown(確保使用此型別的人宣告其確切型別)、never(此型別不可能發生)以及 void(返回 undefined 或沒有返回值的函式)。
你會發現構建型別有兩種語法:介面(Interfaces)和類型別名(Types)。你應該優先選擇 interface。當需要特定功能時,再使用 type。
組合型別
使用 TypeScript,你可以透過組合簡單型別來建立複雜型別。有兩種常用的方法:聯合(Unions)和泛型(Generics)。
聯合型別
透過聯合,你可以宣告一個型別可以是多種型別之一。例如,你可以將 boolean 型別描述為 true 或 false。
tsTrytypeMyBool = true | false;
注意:如果你將滑鼠懸停在上面的 MyBool 上,會看到它被歸類為 boolean。這是結構化型別系統的一個特性。下面會詳細介紹。
聯合型別的一個流行用例是描述允許作為值的 string 或 number 字面量 集合。
tsTrytypeWindowStates = "open" | "closed" | "minimized";typeLockStates = "locked" | "unlocked";typePositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
聯合也提供了處理不同型別的方法。例如,你可能有一個接收 array 或 string 的函式。
tsTryfunctiongetLength (obj : string | string[]) {returnobj .length ;}
要了解變數的型別,請使用 typeof:
| 型別 | 謂詞 |
|---|---|
| string | typeof s === "string" |
| number | typeof n === "number" |
| boolean | typeof b === "boolean" |
| undefined | typeof undefined === "undefined" |
| 函式 | typeof f === "function" |
| 陣列 | Array.isArray(a) |
例如,你可以讓函式根據傳入的是字串還是陣列返回不同的值。
tsTryfunctionwrapInArray (obj : string | string[]) {if (typeofobj === "string") {return [obj ];}returnobj ;}
泛型
泛型為型別提供了變數。一個常見的例子是陣列。沒有泛型的陣列可以包含任何東西。帶有泛型的陣列可以描述陣列中包含的值的型別。
tstype StringArray = Array<string>;type NumberArray = Array<number>;type ObjectWithNameArray = Array<{ name: string }>;
你可以宣告使用泛型的自定義型別。
tsTryinterfaceBackpack <Type > {add : (obj :Type ) => void;get : () =>Type ;}// This line is a shortcut to tell TypeScript there is a// constant called `backpack`, and to not worry about where it came from.declare constbackpack :Backpack <string>;// object is a string, because we declared it above as the variable part of Backpack.constobject =backpack .get ();// Since the backpack variable is a string, you can't pass a number to the add function.Argument of type 'number' is not assignable to parameter of type 'string'.2345Argument of type 'number' is not assignable to parameter of type 'string'.backpack .add (23 );
結構化型別系統
TypeScript 的核心原則之一是型別檢查關注的是值的形狀。這有時被稱為“鴨子型別(duck typing)”或“結構化型別(structural typing)”。
在結構化型別系統中,如果兩個物件具有相同的形狀,它們就被視為同一型別。
tsTryinterfacePoint {x : number;y : number;}functionlogPoint (p :Point ) {console .log (`${p .x }, ${p .y }`);}// logs "12, 26"constpoint = {x : 12,y : 26 };logPoint (point );
point 變數從未被宣告為 Point 型別。然而,TypeScript 在型別檢查時會比較 point 的形狀和 Point 的形狀。它們形狀相同,因此程式碼透過檢查。
形狀匹配僅要求物件的欄位子集匹配即可。
tsTryconstpoint3 = {x : 12,y : 26,z : 89 };logPoint (point3 ); // logs "12, 26"constrect = {x : 33,y : 3,width : 30,height : 80 };logPoint (rect ); // logs "33, 3"constcolor = {hex : "#187ABF" };Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'. Type '{ hex: string; }' is missing the following properties from type 'Point': x, y2345Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'. Type '{ hex: string; }' is missing the following properties from type 'Point': x, ylogPoint (); color
類和物件符合形狀的方式沒有區別。
tsTryclassVirtualPoint {x : number;y : number;constructor(x : number,y : number) {this.x =x ;this.y =y ;}}constnewVPoint = newVirtualPoint (13, 56);logPoint (newVPoint ); // logs "13, 56"
如果物件或類具有所有必需的屬性,TypeScript 就會認為它們匹配,而不管具體的實現細節如何。
後續步驟
這是對日常 TypeScript 中使用的語法和工具的簡要概述。從這裡開始,您可以
- 閱讀完整手冊 從頭到尾
- 探索 Playground 示例