TypeScript 與 JavaScript 的關係非比尋常。TypeScript 提供 JavaScript 的所有功能,並在這些功能之上增加了一層:TypeScript 的類型系統。
例如,JavaScript 提供了像 string
和 number
這樣的語言原語,但它不會檢查您是否已一致地指派這些。TypeScript 會執行檢查。
這表示您現有的運作 JavaScript 程式碼也是 TypeScript 程式碼。TypeScript 的主要好處是它可以強調您的程式碼中意外的行為,降低錯誤發生的機率。
本教學課程簡要說明 TypeScript,重點在於其類型系統。
透過推論的類型
TypeScript 了解 JavaScript 語言,而且在許多情況下會為您產生類型。例如,在建立變數並將其指派給特定值時,TypeScript 會使用該值作為其類型。
tsTry
lethelloWorld = "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
的物件,您可以撰寫
tsTry
constuser = {name : "Hayes",id : 0,};
您可以使用 interface
宣告來明確描述這個物件的形狀
tsTry
interfaceUser {name : string;id : number;}
然後,您可以宣告一個 JavaScript 物件符合您的新 interface
的形狀,方法是在變數宣告後使用類似 : TypeName
的語法
tsTry
constuser :User = {name : "Hayes",id : 0,};
如果您提供了一個與您提供的介面不符的物件,TypeScript 會警告您
tsTry
interfaceUser {name : string;id : number;}constuser :User = {Type '{ username: string; id: number; }' is not assignable to type 'User'. Object literal may only specify known properties, and 'username' does not exist in type 'User'.2322Type '{ username: string; id: number; }' is not assignable to type 'User'. Object literal may only specify known properties, and 'username' does not exist in type 'User'.username : "Hayes",id : 0,};
由於 JavaScript 支援類別和物件導向程式設計,TypeScript 也支援。您可以將介面宣告與類別搭配使用
tsTry
interfaceUser {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);
您可以使用介面來註解函式的參數和傳回值
tsTry
functiondeleteUser (user :User ) {// ...}functiongetAdminUser ():User {//...}
JavaScript 中已經有一組可用的基本類型:boolean
、bigint
、null
、number
、string
、symbol
和 undefined
,您可以在介面中使用這些類型。TypeScript 用幾個其他類型來擴充這個清單,例如 any
(允許任何東西)、unknown
(確保使用此類型的某人宣告類型是什麼)、never
(不可能發生此類型)和 void
(傳回 undefined
或沒有傳回值的函式)。
您會看到有兩種建立類型的語法:介面和類型。您應該偏好 interface
。當您需要特定功能時,請使用 type
。
組合類型
使用 TypeScript,您可以透過組合簡單類型來建立複雜類型。有兩種常見的方式可以做到這一點:使用聯集和泛型。
聯集
使用聯集,您可以宣告一個類型可以是多個類型之一。例如,您可以將 boolean
類型描述為 true
或 false
tsTry
typeMyBool = true | false;
注意:如果您將滑鼠游標懸停在上面的 MyBool
上,您會看到它被歸類為 boolean
。這是結構類型系統的特性。以下有更多說明。
聯集類型的一個常見用例是描述一個值允許是 string
或 number
文字 的集合
tsTry
typeWindowStates = "open" | "closed" | "minimized";typeLockStates = "locked" | "unlocked";typePositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
聯集也提供了一種處理不同類型的方法。例如,您可能有一個函式需要一個 array
或一個 string
tsTry
functiongetLength (obj : string | string[]) {returnobj .length ;}
若要得知變數的類型,請使用 typeof
類型 | 謂詞 |
---|---|
string | typeof s === "string" |
number | typeof n === "number" |
boolean | typeof b === "boolean" |
undefined | typeof undefined === "undefined" |
function | typeof f === "function" |
array | Array.isArray(a) |
例如,您可以讓一個函式傳回不同的值,具體取決於傳入的是字串還是陣列
tsTry
functionwrapInArray (obj : string | string[]) {if (typeofobj === "string") {return [obj ];}returnobj ;}
泛型
泛型提供變數給類型。一個常見的範例是陣列。沒有泛型的陣列可以包含任何東西。有泛型的陣列可以描述陣列包含的值。
ts
type StringArray = Array<string>;type NumberArray = Array<number>;type ObjectWithNameArray = Array<{ name: string }>;
你可以宣告自己的類型來使用泛型
tsTry
interfaceBackpack <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 的核心原則之一是,類型檢查專注於值的「形狀」。這有時稱為「鴨子類型」或「結構化類型」。
在結構化類型系統中,如果兩個物件有相同的形狀,它們會被視為是同一個類型。
tsTry
interfacePoint {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
的形狀進行比較。它們有相同的形狀,所以程式碼通過。
形狀匹配只需要物件欄位的子集相符。
tsTry
constpoint3 = {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
類別和物件如何符合形狀之間沒有差異
tsTry
classVirtualPoint {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 日常使用中語法和工具的簡要概述。從這裡,你可以