泛型 指在定義函數、介面或類的時候,不預先指定具體的類型,而在使用的時候再指定具體類型的一種特性。 引入 下麵創建一個函數, 實現功能: 根據指定的數量 count 和數據 value , 創建一個包含 count 個 value 的數組 不用泛型的話,這個函數可能是下麵這樣: function c ...
泛型
指在定義函數、介面或類的時候,不預先指定具體的類型,而在使用的時候再指定具體類型的一種特性。
引入
下麵創建一個函數, 實現功能: 根據指定的數量 count
和數據 value
, 創建一個包含 count
個 value
的數組 不用泛型的話,這個函數可能是下麵這樣:
function createArray(value: any, count: number): any[] {
const arr: any[] = []
for (let index=0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr1 = createArray('a', 3)
const arr2 = createArray(1, 3)
console.log(arr1)
console.log(arr2)
console.log(arr1[0].toFixed(), arr2[0].split(''))
我們創建了一個函數createArray
,傳入2個參數value
和count
,返回any類型的數組,然後定義了一個any類型的空數組arr。接下來我們查看結果
在編譯階段我們沒有報錯是因為,我們把value設置為了any類型,但是當編譯完成後運行時,arr1是字元串,字元串是沒有toFixed
方法的,所以會報錯,那麼我們希望在編譯階段就報錯,就可以使用泛型
使用泛型
// 使用函數泛型
function createArray<T>(value: T, count: number): T[] {
const arr: Array<T> = []
for (let index=0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr1 = createArray<number>(11, 3)
console.log(arr1[0].toFixed())
const arr2 = createArray<string>('AA', 3)
console.log(arr2[0].split(''))
console.log('---------')
console.log(arr2[0].toFixed()) // 報錯,因為字元串沒有toFixed方法
console.log(arr1[0].split('')) // 報錯,因為number沒有split方法
泛型的意思就是類型由用戶自己決定,比如function createArray<T>(value: T, count: number): T[]
,函數createArray
和value
參數和返回類型都由用戶自己決定。
const arr1 = createArray<number>(11, 3)
這句代碼是沒問題,因為規定了number類型,傳入的也是number
當我們將代碼修改成如下代碼:
我們發現報錯了,因為規定了number類型,傳入的卻是字元串11
,
當我們輸入如下代碼,也會報錯
報錯原因如下
所以如果我們使用了泛型,就會避免類型輸入錯誤或者用錯方法
多個泛型參數的函數
一個函數可以定義多個泛型參數
function swap <K, V> (a: K, b: V): [K, V] {
return [a, b]
}
const result = swap<string, number>('abc', 123)
console.log(result[0].length, result[1].toFixed())
泛型介面
interface IbaseCRUD <T> {
// 定義泛型數組data
data: T[]
add: (t: T) => void
getById: (id: number) => T
}
class User {
id?: number;
name: string;
age: number;
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class UserCRUD implements IbaseCRUD<User> {
data: User[] = []
add(user: User): void {
user = {...user, id: Date.now()}
this.data.push(user)
console.log('保存user', user.id)
}
getById(id: number): User {
return this.data.find(item => item.id === id)
}
}
const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)
泛型類
泛型類看上去與泛型介面差不多。 泛型類使用( <>
)括起泛型類型,跟在類名後面。
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
GenericNumber
類的使用是十分直觀的,並且你可能已經註意到了,沒有什麼去限制它只能使用number
類型。 也可以使用字元串或其它更複雜的類型。
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };
console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));
與介面一樣,直接把泛型類型放在類後面,可以幫助我們確認類的所有屬性都在使用相同的類型。
泛型約束
如果我們直接對一個泛型參數取 length
屬性, 會報錯, 因為這個泛型根本就不知道它有這個屬性
// 沒有泛型約束
function fn <T>(x: T): void {
console.log(x.length) // 報錯,因為目前不知道x是什麼類型
}
我們可以使用泛型約束來實現
interface Lengthwise {
length: number;
}
// 指定泛型約束
function fn2 <T extends Lengthwise>(x: T): void {
console.log(x.length)
}
我們需要傳入符合約束類型的值,必須包含必須 length
屬性:
fn2('abc')
// fn2(123) // error number沒有length屬性