TypeScript(九)泛型基礎
目錄
前言
本文收錄于TypeScript知識總結系列文章,歡迎指正!
代碼復用是開發人員老生常談的問題了,我們通過定義變量,使用函數或類減少代碼重復編寫。
在TS中我們可以把編寫一個類型看成是新建一個函數,參數便是今天的主角:泛型,通過傳入不同的參數(類型)控制類型的走向,達到類型復用的目的
定義
泛型類型是指一種不確定的類型,它允許開發者在定義函數、類、接口時不指定具體的類型,而是使用一個占位符類型,等到函數、類、接口被調用時再動態地傳入具體類型,以便讓它們適用于多種不同類型的數據。
使用泛型可以使代碼更加通用、可復用、可擴展,提高代碼的質量和可維護性。
基本用法
泛型使用尖括號<T>表示,其中T可以被任意字母替換,它代表一種類型,在使用時需要將其替換為具體的類型。
還記得之前數組的泛型寫法嗎?
const arr: Array<string> = ["a", "b"]
我們使用尖括號來標注泛型的實際類型
在JS中我們都會定義函數,每個函數的行為走向或許由參數決定,舉個例子
type IObj = {
name?: string
}
interface ISetKey {
(obj: IObj, key: string): IObj
}
const setName: ISetKey = function (obj: IObj, name: string): IObj {
if (!!!obj.name) obj.name = name
return obj
}
const obj: IObj = setName({}, "張三")
上面這段代碼,我實現了一個給對象設置值的函數,乍一看似乎沒啥毛病,但是如果我給上述的代碼增加一個age操作
type IObj1 = {
age?: number
}
const setAge: ISetKey = function (obj: IObj1, age: number): IObj1 {
if (!!!obj.age) obj.age = age
return obj
}
const obj1: IObj1 = setAge({}, 20)
ISetKey這個接口幾乎是要重新寫了,它的結構也要隨著改變,變成
interface ISetKey1 {
(obj: IObj1, key: number): IObj1
}
通過這個小例子我們可以看到,如果我們在ISetKey中使用兩個變量O和K來代表參數的兩個類型,那么我們就可以使接口或者類型達到復用的效果
interface ISetKey<O, K> {
(obj: O, key: K): O
}
使用效果:
const setName: ISetKey<IObj, string>
const setAge: ISetKey<IObj1, number>
掌握了上述的代碼,就說明我們對泛型有了一個認識和入門
泛型命名約定
泛型的定義使用T或U之類的是為了表達特定的含義或約定,目的是增強代碼的可讀性,當然這些是對命名的俗成,泛型的寫法是自由選擇的,沒有硬性要求或規定;下面我會列舉一些常見的寫法供參考
常見的T表示類型(type),U是未知(unknown),N表示數字(number),V是值(value)
此外還有K鍵名,A數組,R函數返回值,M映射等等
泛型&類型別名
既然泛型可以代表一種類型,我們可以將泛型類型參數作為類型別名的一部分與類型別名一起使用
type Animal<T, N> = {
name: T
age: N
}
const animal: Animal<string, number> = {
name: "阿黃",
age: 10
}
上面代碼中我們實現了對Animal類型的動態描述,T表示name的類型,N表示age的類型
泛型&接口
接口中使用泛型和類型別名很相似,同樣是在定義的接口名后面聲明參數,在類型使用時動態傳入具體類型,下面是數組及部分屬性和函數的寫法
interface MyArray<T> {
[num: number]: T
length: number
push: (...item: T[]) => number
forEach: (cb: (item: T, i: number, arr: MyArray<T>) => void) => void
}
const list: MyArray<string> = ["a", "b"]
console.log(list.push("c"));
list.forEach(console.log)
console.log(list);
/*
輸出:
3
a 0 [ 'a', 'b', 'c' ]
b 1 [ 'a', 'b', 'c' ]
c 2 [ 'a', 'b', 'c' ]
[ 'a', 'b', 'c' ]
*/
泛型&函數
泛型函數的寫法和上面類似
// 函數聲明
function getValue<T>(val: T): T {
return val;
}
// 函數表達式
const getValue1 = function <T>(val: T): T {
return val;
}
// 箭頭函數
const getValue2 = <T>(val: T): T => {
return val
}
getValue<string>("abc")
getValue1<number>(123)
getValue2<boolean>(true)
泛型&類
使用泛型來定義類,可以使類中的成員方法和屬性更加靈活和通用,泛型類的使用方式與泛型接口類似,在類名后添加一個<T>來表示泛型類
interface IDog {
name?: string
likeMeat: boolean
}
interface ICat {
name?: string
likeFish: boolean
}
class Animal<Type>{
constructor(public animalType: Type) { }
}
const dog: IDog = {
name: "阿黃",
likeMeat: true
}
const cat: ICat = {
name: "小橘",
likeFish: true
}
new Animal<IDog>(dog)
new Animal<ICat>(cat)
上述代碼中,我們使用Type表示傳入Animal類的動物類型,當傳入IDog時,類的實例會限制其參數傳入IDog的實現;ICat同理。
泛型默認值
在之前的文章中,我們了解過參數的可選性,函數的參數可以指定默認值。泛型的參數亦是如此,在不傳遞泛型類型參數時使用默認值可以替代參數。使用上面類型別名的例子
type Animal<T = string, N = number> = {
name: T
age: N
}
const animal: Animal = {
name: "阿黃",
age: 10
}
結語
本篇文章講述的是泛型基礎,我分別從泛型用法,泛型命名約定,以及泛型與接口、函數、類型別名、類等方面的寫法,以及泛型默認值等方面介紹了泛型的基本使用,希望能夠對你有幫助。
感謝你的閱讀,如果你從文章中學到了知識,還請支持一下博主,有任何問題歡迎私信或評論,謝謝!

浙公網安備 33010602011771號