TypeScript 型別斷言
前言
在前段時間撰寫的「TypeScript 型別註記與型別推論」,因為篇幅關係,所以沒有討論到型別註記中包含的型別斷言(Type Assertion),而這篇文章要來整理補上這個觀念。
型別斷言的寫法
TypeScript 提供兩種型別斷言的語法,而語法上看到關鍵字 as
或者 <Type>(…)
這樣的格式就是斷言的寫法。如下範例:
1 |
|
> 在 React 的 JSX 語法中,如果有使用到 TypeScript,則只能使用 as
這個寫法。因為在 JSX 中 <>
會被視作標籤。
註記與斷言的不同之處
型別註記與型別斷言這兩者的本質相似,但卻是完全不同的概念。
- 型別註記:在宣告變數或函式參數時,明確告訴 TypeScript 編輯器該變數應該是什麼型別。讓編輯器「依循」這樣的型別規則,並會根據此規則幫我們檢查型別。
- 型別斷言:依照我們自身的判斷,明確告訴 TypeScript 編譯器某表達式之結果應該是什麼型別。也就是我們斷定的型別,去「覆蓋」TypeScript 編譯器所推斷的型別。
型別斷言的使用情境
當程式沒辦法推斷出某表達式的運算結果之型別時,我們就可以使用型別斷言。以下舉例幾個情境:
未知結果的函式
假設我們有個函式 fetchDataFromLibrary
,因為它可能根據某個外部資源獲取資料,所以返回的型別不確定;不過我們可能從套件中的文件得知它會回傳某種特定型別(下方範例中是斷言為 string
)。
1 |
|
HTML DOM
在沒有使用前端框架的 TypeScript 環境中,當我們取 HTML DOM 元素並嘗試操作時,TypeScript 會推斷此元素返回的型別為 HTMLElement | null
,所以會出現紅字來提醒。如下範例所示:
1 |
|
為了避免此錯誤提醒,我們可以用型別斷言告訴 TypeScript 該元素一定是 HTMLElement
。不過以這個範例來說,在考量到程式的保護性,還是會建議加上if(element)
的判斷,以防止真的沒取到元素。
1 |
|
API 返回的數據
在開發的過程中,有時會需要串接 API 或第三方套件,因爲 API 返回的數據無法明確推斷(通常會推斷為 any
),這時我們可以使用型別斷言來告訴 TypeScript 預期的數據型別。
下方範例中,我們模擬從 Random User API 獲取使用者資料,而用 fetchDataWithoutAssertion
、fetchDataWithAssertion
這兩個函式來比對是否使用型別斷言的狀況。
(以下有使用到 interface
的寫法,不過在這個 TypeScript 系列中還未提到,所以先簡單提一下定義,它是用來告訴 TypeScript 某個物件會包含哪些屬性跟方法,以及每個屬性的型別,可以把它想像成一個預先架構或藍圖。 不過在下方的範例中,可以先專注在兩個函式所比對的內容,來了解型別斷言的觀念。)
1 |
|
- 未使用型別斷言:在
fetchDataWithoutAssertion
函式中,data
被視為any
型別,所以即便後續的屬性名稱有錯誤,因為 TypeScript 編譯器無法提供屬性或方法的檢查,所以也不會有編譯時期的錯誤提示,需要到執行時期才能知道錯誤。 - 使用型別斷言:在
fetchDataWithAssertion
函式中,通過as RandomUserResponse
將data
斷言為特定型別,TypeScript 能在編譯時進行屬性跟方法的檢查。如果後續引用了不存在的屬性,如範例中看到的nameTypo
、extralarge
,在編譯時期就能直接看到錯誤提示。
結論
當程式沒辦法推斷出某表達式的運算結果之型別時,我們可根據一些資訊來適當加上型別斷言;不過使用斷言也較會有人為的錯誤,因此需比較謹慎。
最後引用 <讓 TypeScript 成為你全端開發的 ACE!> 2-17 頁面的範例:
1 |
|
如上方範例所示,程式從此會認定變數 something
是字串型別,即使在這段程式碼中,變數 something
實際的型別是數字。而這段程式碼在 TypeScript 不會出現任何錯誤訊息,完全是人為的錯誤哩!
參考資料:
- 六角學院 TypeScript 30 天課程
- 書籍:<讓 TypeScript 成為你全端開發的 ACE!>
Photo by Safar Safarov on Unsplash