今天來分享一下ts的泛型,最后來做一個練習
泛型
有時候,我們在書寫某些函數的時候,會丟失一些類型信息,比如我下面有一個例子,我想提取一個數組的某個索引之前的所有數據
function getArraySomeData(newArr, n:number) {let newArr2:any[] = [];newArr.forEach((it, i) => {if (n > i) {newArr2.push(it);}});return newArr2;
}const arr = getArraySomeData(['1112', '1213', '121'], 2);
console.log(arr)
但是這個數組它可以是任意類型的,可以是對象,字符串,數字數組,這個時候不約束的話會存在ts的隱患,這個時候就有了泛型的概念。
概念
泛型的一個概念:
泛型相當于是一個類型變量,在定義時,無法預先知道具體的類型,可以用該變量來代替,只有在調用時,才能確定它的類型
很多時候,TS會智能的根據傳遞的參數,推導出泛型的具體類型
我們可以把剛才的代碼使用泛型改成這種形式
function getArraySomeData<T>(newArr:T[], n:number) {let newArr2:T[] = [];newArr.forEach((it, i) => {if (n > i) {newArr2.push(it);}});return newArr2;
}const arr = getArraySomeData(['1112', '1213', '121'], 2);
console.log(arr)
如果沒有傳遞具體類型,無法完成推導,默認是空對象。
進行傳值
如果我 傳遞值只有ts會根據傳遞的類型進行嚴格類型檢查,如下圖,傳遞的是number,傳遞的就必須都是number
泛型設置默認值
function getArraySomeData<T = number>(newArr:any[], n:number) {}
在類型別名、接口、類使用泛型
回調函數,判斷數組的某一項是否滿足條件,這個某一項可以用泛型來代替
類型別名
type callback<T> = (n:T,i:number) => boolean;
接口
下面手動寫了一個篩選的filter函數
interface callback<T> = (n:T,i:number) => boolean;function filter<T>(arr: T[], callback: callback<T>): T[] {const newArr: T[] = [];arr.forEach((n, i) => {if (callback(n, i)) {newArr.push(n);}});return newArr;
}
類
下面這個例子是一個數組幫助類,通過在類最外面設置了泛型T,來約束里面所有的方法的泛型,之后傳遞的所有的參數泛型以及數組放行,都是傳遞的值。
export class ArrayHelper<T> {constructor(private arr:T[]){}take(n: number): T[] {if (n >= this.arr.length) {return this.arr;}const newArr: T[] = [];for (let i = 0; i < n; i++) {newArr.push(this.arr[i])}return newArr;}shuffle() {for (let i = 0; i < this.arr.length; i++) {const targetIndex = this.getRandom(0, this.arr.length);const temp = this.arr[i];this.arr[i] = this.arr[targetIndex];this.arr[targetIndex] = temp;}}private getRandom(min: number, max: number) {const dec = max - min;return Math.floor(Math.random() * dec + min)}
}
泛型約束
這個是在有些特定場景的時候會需要使用,這里舉一個簡單的例子,我傳遞一個對象,然后我需要把這個對象的時間戳轉換成日期字符串的形式
function dateFormat<T>(obj:T):T{obj.time = util.timeFormat(obj.time);return obj;
}
這個時候,ts會智能報錯,報錯的位置是obj.time的位置,因為泛型可以傳遞的類型是任意的類型,所以我用對象屬性的方式,ts會進行報錯,所以這種情況需要類型的約束
具體怎么做呢,很簡單
interface hasTimePro{time : number
}function dateFormat<T extends hasTimePro>(obj:T):T{obj.time = util.timeFormat(obj.time);return obj;
}
上面通過接口定義要約束的泛型的類型,可以是對象,也可以是其他。然后在泛型里進行繼承,繼承這個接口,這樣就可以對這個泛型進行一個約束。
多泛型
在很多情況下,我們可能參數特別多,要限制的類和接口可能不止一個,所以ts同樣支持可以有多個泛型,具體的使用例子
我希望混合兩個 數組,數組1和數組2,兩個可能都是不一樣的數組,但是兩個數組長度必須一樣,至于傳遞什么類型不管,只負責混合。這里就可以通過英文逗號將泛型隔開,然后設置多個泛型
function mixinArray<T, K>(arr1: T[], arr2: K[]): (T | K)[] {if (arr1.length !== arr2.length) {throw new Error('兩個數組長度不等');}let result: (T | K)[] = [];for (let i = 0; i < arr1.length; i++) {result.push(arr1[i]);result.push(arr2[i]);}return result;
}const result = mixinArray([1, 3, 4], ["a", "b", "c"]);result.forEach(r => console.log(r));
小練習
開發一個字典類(Dictionary),字典里會保存鍵值對的數據鍵值對數據的特點:
- 鍵(key)可以是任何類型,但不允許重復
- 值(value)可以是任何類型
- 每一個鍵對應一個值
- 所有的鍵類型相同,所有的值類型相同字典類對鍵值對數組的操作:- 添加一個鍵值對
- 按照鍵刪除對應的鍵值對
- 循環每一個鍵值對
- 得到當前鍵值對的數量
- 判斷某個鍵是否存在
- 重新設置某個鍵的值,如果不存在,就添加
代碼
-
src
- dictionary.ts
export type CallBack<K, V> = (key: K, val: V) => voidexport class Dictonary<K, V> {private keys: K[] = [];private vals: V[] = [];get size() {return this.keys.length;}set(key: K, val: V) {const i = this.keys.indexOf(key)if (i < 0) {this.keys.push(key);this.vals.push(val);} else {this.vals[i] = val;}}forEach(callback: CallBack<K, V>) {this.keys.forEach((k, i) => {const v = this.vals[i];callback(k, v);});}has(key: K) {return this.keys.includes(key);}delete(key: K) {const i = this.keys.indexOf(key);if (i === -1) {return;}this.keys.splice(i, 1);this.vals.splice(i, 1);} }
- index.ts
import { Dictonary } from "./dictionary";const dic = new Dictonary<string, number>();dic.set('a', 1); dic.set('b', 1); dic.set('a', 12); dic.set('c', 1231);dic.forEach((d,i)=>{console.log(`key:${d},v:${i}`) });dic.delete('b');dic.forEach((d,i)=>{console.log(`key:${d},v:${i}`) });console.log('當前鍵值對的數量:' + dic.size)