作者: 神Q超人
譯者:前端小智
來源:medium
有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。
由於 JavaScript 本身是弱語言,因此在開發上常因為不知道變數的型別是什麼而感到苦惱,即使藉由命名的方式讓變數的定位稍微明確一點,我們還是很難一眼就知道他的型別甚至當此變數是一個 object
時我們更難知道里面有哪些 key
,因此大家漸漸開始使用 TypeScript 作為主要的開發工具。
不曉得大家在利用 TypeScript 進行開發時,有沒有覺得 TypeScript 在檢查型別這塊特別惱人,雖然知道這些型別檢查的舉動是非常好的,可以幫助我們減少許多可能會發生的潛在錯誤,今天就要來談談當我們在開發上遇到這種問題時該如何解決。
場景一
不曉得大家有沒有遇過這種問題,今天想要讓這個變數檢視是否符合 enum
中的某一個值,結果 TypeScript 就噴錯給你看了,像下面這樣。
其實要解決上面的紅字方法非常多,首先是開大絕使用 @ts-ignore
讓錯誤消失,當然這個方法非常不好,等於是叫 TypeScript 不要檢查下面這行了。
這時候可能會想到另一個方法,上面的錯誤資訊是說 male
沒有被 assign
到 GENDER
這個 type
,所以我只要強制塞給他這個 type
就好,就像這樣:
可是這樣寫仍然不好,等於你強制轉變這個變數了,讓這個變數失去了彈性,接下來我們介紹比較好用的方法,就讓我們繼續看下去吧!
Type Guard
首先要介紹的是 Type Guard,Type Guard 顧名思義就是型別的看守者,剛剛 TypeScript 會報錯就是因為 type 不一樣,所以只要我們建立一個型別的看守者,讓 TypeScript 知道這個變數一定會符合我 enum
中的某一個 value
時,這時候就不會出現紅字了,而通常 Type Guard 會寫成一個 function
像這樣:
const assertsIsGender = (gender: any) : gender is GENDER => {
return Object.values(GENDER).includes(gender)
}
這時候我們可以發現 gender
這個變數已經從 string
type 變成 GENDER
type 了,所以即便我很無聊的再做一次 includes
的判斷 TypeScript 也不會報任何錯誤了。
這邊我在指定 gender
這個值之前先指派這個變數是一個 string
type,這個動作很重要,如果沒有先指派變數型別再給值的話這個變數就沒辦法順利改變 type
了。
場景二
不曉得大家有沒有遇過在 API 回傳的資料,也會因為資料對應到的 enum
的值不同而發生錯誤,像下面這樣:
有了上面 Type Guard 的觀念後,這時候的讀者一定知道要寫一個 function
來處理這段錯誤資訊:
的確錯誤資訊沒有了,但很奇怪的是 gender
竟然變成 never
type 了,而這個就是 Type Guard 會做到的一個型別保護機制叫:Narrowing。
型別收窄(Narrowing)
Narrowing 翻成白話文就是型別收窄,在 TypeScript 的世界中每一個 enum
基本上都是獨立存在彼此之間是沒有交集的,關係圖就像下面這樣:
所以要進行兩個 enum 間的型別轉換就很容易產生出一個可能不會存在的型別,對於可能不會存在的類別 TypeScript 把這個型別定義為 never
,而這時候當我們使用了 Type Guard 的技巧,TypeScript 就會自動把型別收窄成 never type
,而不是自動轉換成另一個 enum
了。
當然聰明的你可能會這樣想:那我只要把 function return
定義成另一個 enum
不就好了,這樣就可以確保我 Type Guard 的結果一定會型別轉換成我想要的 enum,像下面這樣:
這樣寫看起來的確沒有什麼問題,我們想要的結果也從型別收窄變成了型別轉換,但這樣做其實就有點不太符合 Type Guard 的精神,畢竟 Type Guard 要做的是型別檢查而不是型別轉換,而且假如我們要做的是型別轉換,這樣寫也會讓這個 function
的複用性不高,因此我們接下來要介紹比較好的型別轉型方法。
Mapper enum
首先我們可以先想想如何讓型別轉換這件事被複用,我們不妨把想法簡單化,就是建立一個 function
把 A
型態轉換成 B
型態,而這時候就必須要利用 TypeScript 中的 Generics 泛型這個技巧了,像下面這樣:
const createEnumMapper = <T>(mapping: T) => (value: keyof T | null) : T[keyof T] | undefined => {
return value === null ? undefined : mapping[value]
}
這個 createEnumMapper
的 function 是一個 currying function
,第一個變數傳入的是 enum 本身,這時候 TypeScript 的 Generics 就會知道我的 T
就是跟 enum
本身有關。
為了讓這個 Generics
可以正確的把兩個 enum mapping
起來,我們必須要先建立一個 object
把兩個 enum
的 key value
配對像下面這樣:
const mapper = {
[BE_GENDER.MALE]: FE_GENDER.MALE,
[BE_GENDER.FEMALE]: FE_GENDER.FEMALE
}
由於我們上面的 mapper
是把 enum 的 value
當成 key
,所以我們只要帶入 data
的值就可以直接轉換了,像下面這樣:
這時候就可以發現我們成功的把 BE_GENDER
type 的值轉成 FE_GENDER
type 的值了,而且也不需要動用到 Type Guard 的觀念。
總結
今天介紹了 TypeScript 中用來檢查型別的方法,假如讀者日後遇到類似這種問題不妨可以多加利用 Type Guard 進行檢查,而不是直接開大絕用 @ts-ignore
或者 as
這兩種方法,除了介紹型別檢查外也介紹瞭如何進行型別轉換,希望這些方法都可以讓讀者未來在使用上都不會有太多的問題。
我是刷碗智,新的一年,我們一起刷刷刷。
程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
https://medium.com/onedegree-...
交流
有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。
本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。