這個 TypeScript 技巧將讓你大開眼界
TypeScript 是一個了不起的工具,可以用來操縱現有數據并開發(fā)出優(yōu)秀的實踐方法。
今天我們將探索如何從字符串數組中正確提取全名,以確保輸出的類型安全和干凈。
問題
我們檢查一下這段代碼并理解它的問題:
const names = ["Daniel Craciun", "John Doe", "Harry Pigeon"]
const findName = (surname: string) => {
return names.find((name) => name.includes(surname))
}
// 我們可以傳入任意字符串,這是不理想的。
console.log(findName("Craciun")) // 輸出: Daniel Craciun
console.log(findName("Doee")) // 輸出: undefined
這段代碼使用了一個名字數組進行搜索。findName 函數接受一個姓氏字符串并返回相關的全名。問題在于當你輸入 "Doee" 進 findName 函數時會出現。
這個不起眼的拼寫錯誤會導致 undefined 輸出,這可能會在后續(xù)導致錯誤,因為沒有任何機制阻止我們犯這個錯誤。
這時 TypeScript 就派上用場了。
如果我們確保 findName 只接受字面的姓氏,即 Craciun, Doe, Pigeon,那么當我們輸入 names 數組中不存在的輸入(如 "Doee")時,編譯器就會報警。
解決方案
我們已經確定,findName 的有效參數需要包含所有現有的姓氏。為了實現這一點,我們創(chuàng)建一個名為 ExtractSurname 的泛型類型。
ExtractSurname 的代碼可能看起來很復雜,但我們將逐步解析它:
type ExtractSurname<T extends string> = T extends `${infer Firstname} ${infer Surname}` ? Surname : null
這里 ExtractSurname 接受一個泛型參數 T,該參數使用 extends 操作符指的是任何字面字符串。
在 ExtractSurname<"Daniel"> 中,T 的值等于 "Daniel"。
接下來我們應用 TypeScript 的三元操作符,這類似于 JavaScript 的三元操作符,但我們比較的是類型而不是實際數據。
我們知道 names 數組的格式是 “<Name> <Surname>”,所以這里的 infer 關鍵字從 T 中提取子類型。
在ExtractSurname<"Daniel Craciun">中:
infer Firstname = "Daniel"
infer Surname = "Craciun"
最后,如果輸入滿足我們的 “” 格式,則返回姓氏作為類型,否則返回 null
好了,我們的 ExtractSurname 類型已經準備好了。
現在我們需要一個 Surname 類型來表示 names 中的所有姓氏。
type ExtractSurname<T extends string> = T extends `${infer Firstname} ${infer Surname}` ? Surname : null
const names = ["Daniel Craciun", "John Doe", "Harry Pigeon"] as const
type Surname = ExtractSurname<(typeof names)[number]>
names 滿足 ExtractSurname 的 “<Name> <Surname>” 格式。
我們使用 as const 將 names 的類型縮小為字面字符串數組。這意味著我們將 names 的類型從 string 強制轉換為:
readonly ["Daniel Craciun", "John Doe", "Harry Pigeon"]
參數 (typeof names)[number] 表示 names 中每個索引元素的類型:
"Daniel Craciun" | "John Doe" | "Harry Pigeon"
最后,這是 Surname 的結果類型:
type of Surname: "Craciun" | "Doe" | "Pigeon"
最后一步是更新我們之前定義的 findName 函數,用一個新的 findNameUsingSurname 函數,如下所示:
// 接受實際的 `Surname` 而不是一般的字符串。
const findNameUsingSurname = (surname: Surname) => {
// 注意:我們需要后綴操作符 "!" 來斷言 `find` 函數不會返回 undefined 值。
return names.find((name) => name.includes(surname))!
}
// 唯一可接受的輸入:"Craciun", "Doe", "Pigeon" = 最大類型安全
const fullName = findNameUsingSurname("Craciun")
// 輸出: "Daniel Craciun"
console.log(fullName)
這就是我們預期的 TypeScript 編譯器的魔法:
結論
TypeScript 是一個強大的工具,可以確保代碼的類型安全性。通過使用 ExtractSurname
類型,我們能夠確保 findName
函數只接受有效的姓氏,從而避免拼寫錯誤和潛在的運行時錯誤。這個技巧不僅提高了代碼的安全性,還增強了開發(fā)效率。希望你能在項目中嘗試這個技巧,并體驗它帶來的便利和強大功能。