對於前端從業者來說,TypeScript(以下簡稱 TS)已經不算是新技術。
Vue3 的源碼基於 TS 編寫, Angular 項目默認支持 TS 等。它出現的頻率越來越高,而學習難度並不大,大概一個周末可以熟悉。
投入少,產出大,所以最好還是花點時間學習一下。
本文根據 TS handbook 整理出 TS 關鍵的知識點,並對某些部分作了擴展,希望能幫助大家學習理解。
前言
在學習 TS 之前,需要理解它的兩個特點:
- TS 是 JS 的超集
- TS 給 JS 帶來類型系統
這意味着 TS 的根基是 JS,是在 JS 的 基礎上添加了類型系統。
類型聲明
類型系統的一個特點是使用類型聲明。

觀察上述代碼,在 JS 中,使用「等號=」給變量賦值。
經過 TS 的擴展,可以使用「冒號:」給變量賦類型。代碼中聲明變量 foo 的類型是 string。
類型校驗
類型系統的另一個特點是進行類型校驗。
在 TS 中,需要校驗「等號=」左右的類型是否匹配。

如上述代碼所示,值存在明確的類型,TS 會校驗左右兩側的類型是否匹配,若不匹配則提示錯誤。
在了解這些前置知識后,來看看具體的 TS 知識點。
一、基本類型
繼承 JS 的基本類型如:string、number、boolean、undefined、null。擴展了其他基本類型如:any、never、void。
1.1 string
表示類型為字符串

1.2 number
表示類型為數字

1.3 boolean
表示類型為布爾類型

1.4 undefined
表示類型為 undefined

1.5 null
表示類型為 null

1.6 void
表示類型為 undefined 或 null。
1.6.1 對於變量
一個聲明為 void 類型的變量,只能被賦值為 undefined 或 null。這種應用場景較少。

1.6.2 對於函數
void 用於表示函數沒有返回值,只是執行某些操作。這是 void 的普遍應用場景。

一個沒有顯式返回的函數,默認 return undefined。符合 void 類型只能被賦值為 undefined 或 null。
1.7 never
表示永遠不存在值。
典型例子:一個只會拋出異常的函數,它的返回值並不存在。

執行函數則拋出錯誤,連 undefined 都不會返回。
1.8 any
表示可能為任何類型。
典型例子:不確定變量的類型或類型是動態的。

二、引用類型
TS 中的引用類型,除了 JS 中的數組類型、對象類型,還擴展了枚舉類型,元祖類型。
2.1 數組類型
表示由某(些)類型組成的數組。有兩種寫法:方括號表示法和尖括號表示法。
2.1.1 方括號表示法

表示 arr 的類型是由數字組成的數組。
2.1.2 尖括號表示法
使用 Array<元素類型>,這是數組泛型的使用形式,關於泛型后續會展開。

2.2 只讀數組
使用 ReadonlyArray<屬性類型> 定義只讀數組。只讀數組只能在數組初始化時定義其值,創建后不能進行修改。

只讀數組和常規數組類型 Array<T> 類似。區別在於:常規數組存在修改數組的方法,只讀數組不存在修改數組的方法。
2.3 object
表示類型為對象。除了 string、number、bigint、boolean、 undefined、null、symbol 基本類型外的引用類型。

2.4 元組
由定長數組構成,數組中的元素是某(些)類型。

2.5 枚舉
使用關鍵字 enum 定義枚舉類型。默認枚舉值從 0 開始,可以手動賦值。

三、類型斷言
明確告訴 TS 某個值的類型。有兩種寫法:尖括號寫法、as 寫法。
尖括號寫法

as 寫法

注意事項
類型斷言和類型轉換是有明確區別,不能將它理解成類型轉換。
在闡述此注意事項之前,先引入另一個概念:聯合類型。

在上述代碼中,string | number 表示的就是一個聯合類型,意味着 answer 的類型可以為 string 或 number。
下面逐步解釋類型斷言和類型轉換之間的區別。

上述代碼中,string | number 聯合類型包含 string 類型,所以這兩種類型之間存在聯系。使用 as 將聯合類型斷言成更加具體的 string 類型。

上述代碼中,將 number 類型斷言為 string | number 類型。同樣是因為兩種類型之間存在聯系,所以也允許斷言。相當於將一個具體的 number 類型斷言成更加廣泛的聯合類型。

而將 string 類型斷言為 number 類型,這是兩種不同的類型,沒有任何聯系,斷言會提示錯誤。
觀察上述三個例子,斷言發生在兩種類型存在聯系的情況,它並不是將一種類型轉換成另一種類型。
有些腦瓜子靈活的朋友會想到,借助兩次斷言來進行類似的類型轉換。

這是欺騙了 TS 校驗,后果只能自己承擔。
四、接口
上面提到使用 object 描述對象類型。但是,對於具有復雜結構的對象、函數。上述的 object 類型力有不逮。

上述代碼中,即使是空對象,也能通過 object 類型校驗,而不會校驗對象的結構是否能滿足后續使用。
此時,就需要使用接口。在 TS 中,接口是描述值的結構。

上述代碼中,就定義了一個接口來描述參數 o,要求它是一個對象,含有 name 屬性,且屬性值是字符串。
所以,不符合此結構的空對象 {} 就提示錯誤。
一般來說,使用關鍵字 interface 來定義接口。
重寫上述接口 ——

4.1 固定屬性
在接口中,使用 屬性名:屬性類型 的結構定義固定屬性。

如上述代碼所示,傳入的對象需要具有 name 和 age 兩個屬性,否則會報錯。
4.2 可選屬性
在接口中,使用 屬性名?:屬性類型 的結構定義可選屬性。顧名思義,可選屬性可以存在,也可以不存在。

4.3 只讀屬性
在接口中,使用 readonly 屬性名:屬性類型 的結構定義只讀屬性。只讀屬性只能在屬性初始化時定義其值,定義后不能進行修改。

4.4 額外檢查
TS 會對對象字面量進行額外的屬性檢查。

在上述代碼中,接口 Point 定義了兩個可選屬性,對象字面量中屬性 x 和接口兼容,屬性 z 是多余無意義的。
雖然實際的屬性比接口定義的多,按照常規理解,這是可以通過類型校驗,但事實卻相反。
TS 對於對象字面量是會進行額外的屬性檢查,體現在:
當對象字面量賦值給變量或它直接作為參數傳遞給函數時,如果對象字面量的屬性沒有在接口中定義,則會報錯。
換句話說,對於對象字面量,當它直接賦值給變量和函數參數時,它的屬性不能比接口描述的多。
這里重點是直接賦值,如果像例子中,先將對象字面量賦值給變量,再通過變量傳參,這樣間接的方法可以繞過額外的檢查。
4.5 可索引類型
可索引類型包括字符串索引類型與數字索引類型。
4.5.1 字符串索引類型
字符串索引類型具有字符串索引簽名。

4.5.2 數字索引類型
數字索引類型具有數字索引簽名

4.5.3 混合索引簽名
屬性和索引簽名可以形成混合索引簽名,但是屬性需要和索引簽名類型匹配。

另外,TS 允許同時使用上述兩種簽名,但是數字索引返回值的類型,它必須是字符串索引返回值類型的子類型。
因為對於 JS 來說,當使用數字索引時,會將它轉換成字符串進行索引。所以它們需要保持一致。

上述代碼中,ThreeD 是 TwoD 的子類型,所以接口 PointA 正確。
4.5.4 只讀索引簽名
可以將索引簽名設置為只讀,只能在數組初始化時定義其值,創建后不能進行修改。

4.6 函數類型
函數類型具有調用簽名。

如上述代碼所示,調用簽名包括參數列表和返回值類型。
對於函數類型來說,它校驗的值當然是函數 ——

如上述代碼所示,函數的參數名可以與簽名的參數名不同。關鍵是對應位置的參數類型需要相同。
4.7 類類型
這里的概念有些復雜,如果有良好的 JS 基礎,會較易理解。
類的關鍵字是 Class,由 ES6 開始引入,並逐步完善。本質上 Class 屬於語法糖,是基於 prototype 原型鏈實現的。

而無論是 ES5 或 ES6,屬性 age 是在創建的實例上,而方法 getAge() 是在實例的原型鏈上。

而類本身,它是不存在 age 屬性 和 getAge() 方法的。

當然,我們也可以給類本身定義屬性和方法。 但給類本身定義的屬性和方法並不能通過實例直接訪問。

所以,類與實例的屬性和方法是割裂的。
TS 將描述類的屬性和方法部分稱為類的靜態部分類型,將描述實例的屬性和方法部分稱為類的實例部分類型。

在上述代碼中,使用關鍵字 implements 描述類實現了接口。更准確的是:描述 類的實例部分 實現了接口。
實現該接口的類的實例,它是具有 age 屬性,getAge() 方法。而類本身(即類的靜態部分)具有何種屬性與方法,上述代碼沒有進行定義。
所以,這里所說的類類型,實際上是類(實例的)類型。
關於如何定義類的靜態部分的類型,在后續會詳細介紹。
4.8 接口繼承
接口的可以使用關鍵字 extends 定義繼承。一個接口可以繼承多個接口。

4.9 混合類型
一個對象可能混合多種類型。
例如定義一個帶版本號的函數——

值得注意,上述使用類型斷言,將函數斷言為 Fn,即使 fn 在創建時並不存在 version 屬性。
4.10 接口繼承類
當接口繼承類類型時候,表現在繼承類的成員和結構,但不包括其實現。
接口繼承類的一個場景是,定義一個子類的類類型。

結語
由於文章篇幅問題,全文拆分成上下兩篇發布。
本篇主要介紹了 TS 的基本類型,引用類型、類型斷言、接口等知識點,了解上述的知識點可以閱讀部分 TS 代碼。
下篇涉及函數、類、泛型等稍微復雜的知識點。