目錄
- 概念導讀
- 泛型函數
- 多個泛型參數
- 泛型約束
- 泛型別名
- 泛型接口
- 泛型類
- 總結:
概念導讀
泛型
(Generics)是指在定義函數、接口或類
的時候,不預先指定具體的類型
,而在使用的時候再指定類型
的一種特性。使用泛型 可以復用類型并且讓類型更加靈活
泛型實現類型參數化:
在定義這個函數時, 我不決定這些參數的類型
而是讓調用者以參數的形式告知,我這里的函數參數應該是什么類型
把類型作為參數,放在尖括號中
泛型的基本使用
案例講解:
當我們封裝一個函數的時候,可能由于業務需求的不同,向函數內,傳遞的參數類型也不相同。而TS 的語法規范是,在函數定義的時候就需要為每個待接收的形參以及函數的返回值指定類型,如果在我們調用執行函數之前,就將類型給指定死了的話,這就大大降低了,函數的復用性了。
這時候,就可以使用
泛型
,來實現這樣的需求,將函數所需參數的類型,延后到,當我調用函數的時候,可以根據不同的業務需求再指定其參數類型。這樣一來,將大大的提高函數的復用靈活性。
如下案例:
泛型函數
普通函數定義法
function createArray<T>(length: number, value: T): Array<T> {let result: T[] = [];for (let i = 0; i < length; i++) {result[i] = value;}return result;
}createArray(3, 'x'); // ['x', 'x', 'x']
箭頭函數定義法
let append = <T>(val: T, num: number): Array<T> => {const arr: T[] = []for (let index = 0; index < num; index++) {arr.push(val)}return arr;
}
console.log(append<string>("一段字符串", 9));
//['一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串']
console.log(append<number>(10086, 9));
//[10086, 10086, 10086, 10086, 10086, 10086, 10086, 10086, 10086]
我們在函數名后添加了 < T >,其中 T 用來指代任意輸入的類型(
泛型
),后面的參數類型,以及函數返回值的類型,都可以 使用 T 泛型來定義站位。
console.log(append< string >("一段字符串", 9));
在函數調用的時候再為其指定泛型所具體代表的類型
,也可以不手動指定,而讓類型推論自動推算出來(推薦手動指定,使其代碼更加清晰明了)
多個泛型參數
定義泛型的時候,可以一次定義多個類型參數,同樣的在調用的時候再為其指定參數類型,
多泛型參數多用于元組類型的數據處理
普通函數寫法:
function swap<T, U>(tuple: [T, U]): [U, T] {return [tuple[1], tuple[0]];
}console.log(swap<number, string>([7, 'seven']));
箭頭函數寫法:
let swap = <T, U>(tuple: [T, U]): [U, T] => {return [tuple[1], tuple[0]];
}console.log(swap<string, number>(["字符串", 100]));
泛型約束
由于泛型,表示數據的類型的待定的,由于事先不知道它是哪種類型,所以不能夠隨意操作一個待定數據類型身上的方法屬性,如果傳進來的數據類型沒有這個屬性方法,則就會引起報錯。
如下案例:
function loggingIdentity<T>(arg: T): T {console.log(arg.length);return arg;
}// 報錯: index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.
上例中,泛型 T 不一定包含屬性 length
,所以編譯的時候報錯了。
這時候,我們就可以通過接口
對泛型進行約束,只允許這個函數傳入那些包含 length 屬性的變量。這就是泛型約束:
interface Lengthwise {length: number;
}function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length);return arg;
}
上例中,我們使用了 extends 約束了泛型 T 必須符合接口 Lengthwise 的形狀,也就是必須包含 length 屬性。
此時如果調用 loggingIdentity 函數的時候,傳入的 arg 數據類型身上 不包含 length 屬性,那么在編譯階段就會報錯了,這樣一來就大大降低了,會發生在函數體內部的錯誤了。
補充
:
多個類型的參數之間也可以互相約束
function copyFields<T extends U, U>(target: T, source: U): T {for (let id in source) {target[id] = (<T>source)[id];}return target;
}let x = { a: 1, b: 2, c: 3, d: 4 };copyFields(x, { b: 10, d: 20 });
上例中,我們使用了兩個類型參數,其中要求 T 繼承 U
,也相當于,U 約束了 T
,這樣就保證了 U 上不會出現 T 中不存在的字段。
泛型別名
在類型別名 type 的后面使用<T>
即可聲明一個泛型參數,接口里的其他成員都能使用該參數的類型
type len = {length: number
}let fun = <T extends len>(x: T): number => {return x.length
}
console.log(fun<string>("486789413"));//9
泛型接口
前面提到過,
interface
是我們在使用 TypeScript 的時候,用來定義接口
的關鍵字。
如:
interface Person {name: string;age: number;
}
const dome: Person = {name: "sunny",age: 18,
};
上面示例的代碼中用聲明接口限制了一個對象,我們也可以通過泛型對我們的接口進行改造
。
這時候我們通過定義泛型的方式,定義 T ,U 兩個占位符,使得我們的接口具備了泛型,更加靈活。但是我們發現 泛型接口和泛型函數不一樣,像上圖中這樣使用,有一個地方報錯了,提示我們需要傳入類型參數,而不能依賴自動的類型推斷,來確實對象屬性的類型,其實這也是可以理解,因
為我們對一個對象的限制,主要就是限制對象的屬性類型,如果我們不確定類型,那我們傳入的一切類型都是有效的了。就起不到類型的限制,而前面我們使用泛型函數,雖然沒有顯示聲明類型,但是我們達到了對函數入參和出參進行了統一。
interface Person<T, U> {name: T;age: U;
}
const dome: Person<string, number> = {name: "sunny",age: 18,
};
所以定義泛型接口的時候,正確的寫法應該是 對其傳入了兩個確定的類型
。T,U的類型也因此確定了。
泛型接口來約束函數:
interface Person<T> {(value: T): Array<T>
}const printFun: Person<string> = <T>(value: T): Array<T> => {let arr: T[] = [];arr.push(value)return arr;
};
console.log(printFun("233")); //['233']
泛型類
與泛型接口類似,泛型也可以用于類的類型定義中:
class information<T, U>{name: Tage: Uconstructor(name: T, age: U) {this.name = name;this.age = age}
}
let c = new information<string, number>("張三", 48)
泛型參數的默認類型:
在 TypeScript 2.3 以后,我們
可以為泛型中的類型參數指定默認類型。當使用泛型時沒有在代碼中直接指定類型參數,從實際值參數中也無法推測出時,這個默認類型就會起作用。
class information<T = string, U = number>{name: Tage: Uconstructor(name: T, age: U) {this.name = name;this.age = age}
}
let c = new information("李四", 25)
總結:
泛型德優勢是什么?
增加類型的復用性和靈活性。
泛型實現的基本方法是什么?
- 找到不確定類型的部分,為其定義泛型參數
- 傳入參數時,為泛型指定具體的類型
泛型約束的作用是什么?
既可以,保留泛型的靈活性,又限制了一定的規范。
注明
:在正式開發中,泛型的使用場景非常多…
🚵?♂? 博主座右銘:向陽而生,我還在路上!
——————————————————————————————
🚴博主想說:將持續性為社區輸出自己的資源,同時也見證自己的進步!
——————————————————————————————
🤼?♂? 如果都看到這了,博主希望留下你的足跡!【📂收藏!👍點贊!??評論!】
——————————————————————————————