嘿嘿,小夥伴們,今天我們要來解決 TypeScript 的一個小小挑戰!
這次的錯誤來自於型別系統的衝突,尤其是當我們在檢查物件屬性時,TypeScript 有時會有點「任性」。不過別擔心,我們會一一解決,讓你不再踩坑!👀
我們這段程式碼定義了兩組 URL 格式的物件:VideoFormatURLs
和 SubtitleURLs
,並搭配幾個用來檢查屬性是否存在的函式。
主要目的是確保在讀取或使用物件屬性時,是安全且可預期的。但執行時遇到一個有趣的錯誤,來看看是什麼吧!
type VideoFormatURLs = {
format360p: URL,
format480p: URL,
format720p: URL,
format1080p: URL
}
function isFormatAvailable(
obj: VideoFormatURLs,
key: string
): key is keyof VideoFormatURLs {
return key in obj
}
declare const videoFormat: VideoFormatURLs
function loadFormat(format: string) {
if(isAvailable(videoFormat, format)) {
videoFormat[format].pathname.toUpperCase
}
}
type SubtitleURLs = {
english: URL,
german: URL,
french: URL
}
function isSubtitleAvailable(
obj: SubtitleURLs,
key: string
): key is keyof SubtitleURLs {
return key in obj
}
function isAvailable<Obj>(
obj: Obj,
key: string | number | symbol
): key is keyof Obj {
return key in obj
}
Type 'Obj' is not assignable to type 'object'.
這個錯誤告訴我們,isAvailable
函式中的泛型 Obj
沒有被明確限制為物件型別。
TypeScript 要求 keyof
的操作對象必須是一個物件類型,但我們的 Obj
缺少這樣的約束,導致編譯錯誤。
isAvailable
函式中。in
操作符來檢查屬性是否存在,但 TypeScript 對泛型類型不太放心。keyof
需要的是一個物件類型,而 Obj
並沒有被明確限制為物件型別,因此會噴這個錯誤。問題定位: 檢查 isAvailable
函式簽名,發現 Obj
沒有被限制為物件類型。
解決方法: 在泛型 Obj
後添加 extends object
,明確告訴 TypeScript 這是一個物件,避免型別錯誤。
我們需要告訴 TypeScript,Obj
一定是一個物件類型。只要稍微改寫一下函式簽名,讓泛型 Obj
確保它繼承自 object
就搞定啦!
修正後:
function isAvailable<Obj extends object>( // 加上 extends object
obj: Obj,
key: string | number | symbol
): key is keyof Obj {
return key in obj
}
這樣一來,TypeScript 就知道 Obj
確實是物件了,不會再對我們使用 in
操作符吹毛求疵啦!
isFormatAvailable
和 isSubtitleAvailable
: 這兩個函式的目的是用來檢查某個特定格式或字幕是否存在於物件中。
當我們確定 key
是物件的屬性時,TypeScript 會自動推斷該屬性的型別,這樣代碼就更安全。
共用的 isAvailable
函式: 因為我們有多種不同的物件需要檢查屬性,所以使用泛型的方式來實現一個通用的 isAvailable
函式。
這樣可以減少重複程式碼,並保持更好的可讀性。
type VideoFormatURLs = {
format360p: URL,
format480p: URL,
format720p: URL,
format1080p: URL
}
function isFormatAvailable(
obj: VideoFormatURLs,
key: string
): key is keyof VideoFormatURLs {
return key in obj
}
declare const videoFormat: VideoFormatURLs
function loadFormat(format: string) {
if (isAvailable(videoFormat, format)) {
videoFormat[format].pathname.toUpperCase() // 別忘了呼叫函式
}
}
type SubtitleURLs = {
english: URL,
german: URL,
french: URL
}
function isSubtitleAvailable(
obj: SubtitleURLs,
key: string
): key is keyof SubtitleURLs {
return key in obj
}
function isAvailable<Obj extends object>( // 修正泛型約束
obj: Obj,
key: string | number | symbol
): key is keyof Obj {
return key in obj
}
別忘了 ()
:在使用屬性的方法時,不要忘記加上 ()
,像是 toUpperCase()
,少了這個小括號就只會拿到函式本身,結果當然不如預期啦!
類型限制是你的好朋友:為泛型加上約束,不僅是為了避免錯誤,也是對程式行為的自信保證哦!這麼一來,TypeScript 的型別檢查就能幫你避開更多潛在的問題。
TypeScript: TS Playground - An online editor for exploring TypeScript and JavaScript
試著在自己的 TypeScript 專案中應用這些技巧,透過調整型別和加強函式的安全性,來強化你的程式碼吧!
extends object
來告訴 TypeScript 你的意圖,避免不必要的錯誤。()
:別忘記調用函數或方法時要加上小括號,避免出現未預期的錯誤。這樣,你就能輕鬆掌握 TypeScript 的型別陷阱,變得更強大!相信自己,每一個 debug 都是邁向程式大神的重要一步!🚀