有时,书写某个函数时,会丢失一些类型信息(多个位置的类型应该保持一致)或有关联的信息,大多是由 any 引起。泛型就应运而生。
泛型:是指附属于函数、类、接口、类型别名之上的类型。也不会在编译结果中出现。
说白了就一句话,泛型就是为了:动态的传递信息!
很多时候,TS会智能的根据传递的参数,推导出泛型的具体类型。
如果无法完成推导并且又没有传递具体的类型,默认为空对象类型{},因为它的兼容是最好的(鸭子辨型法),但是现在提示的是unknown
在函数中使用泛型
在函数名之后,写上 <泛型名称(一般写T啥的) = 默认值,如果你想写的话>
泛型就相当于是一个类型变量,在定义时无法预知是何种类型。只有在调用函数的时候,才传入具体类型,才能知道是什么类型。
但是只要是在内部(类/接口/函数/函数参数里),则直接使用泛型名称,千万不要再加左右尖括号!!
在类、接口、类型别名中使用泛型
直接在对应名称后写上 <泛型名称(一般写T啥的)>
function take<T = number>(arr: T[], n: number): T[] {
if (n >= arr.length) {
return arr;
} else {
const newArr: T[] = [];
for (let i = 0; i < n; i++) {
newArr.push(arr[i]);
}
return newArr;
}
}
const newArr = take([2, 3, 4, 5], 3);
console.log(newArr);
newArr.forEach(item => {//没有使用泛型前, 这里就丢失了item的类型信息
console.log(item);
});
//回调函数:判断数组中的某一项是否满足条件
// type callback<T> = (n: T, i:number) => boolean;
interface callback<T> {
(n: T, i:number): boolean
}
function filter<T>(arr: T[], callback: callback<T>): T[]{
const newArr: T[] = [];
arr.forEach((item, i) => {
if(callback(item, i)){
newArr.push(item);
}
})
return newArr;
}
const arr = [3,4,6,9,8];
console.log(filter(arr, item => item%2 !== 0));
对于类:
export class ArrayHelper<T>{
//把T写到最外面,则作用域这整个类
constructor(private arr:T[]){
}
take(n: number): T[] {//但是如果在这里给take后面加上泛型,则就按到时传入的类型算
if (n >= this.arr.length) {
return this.arr;
} else {
const newArr: T[] = [];
for (let i = 0; i < n; i++) {
newArr.push(this.arr[i]);
}
return newArr;
}
}
shuffle(){
for(let i = 0, len = this.arr.length;i<len;i++){
for (let i = 0; i < len; i++) {
const targetIndex = this.getRandomNum(0, len);
[this.arr[i], this.arr[targetIndex]] = [this.arr[targetIndex], this.arr[i]];
}
}
return this.arr;
}
private getRandomNum(min: number, max: number) {
const sub = max - min;
return Math.floor(Math.random() * sub + min);
}
}
//另外一个文件
import { ArrayHelper } from "./ArrayHelper";
const helper = new ArrayHelper(['3',{5:3},4,3,2,8]);
console.log(helper.shuffle());
console.log(helper.take(2));
泛型约束
利用 泛型继承 接口/类型别名等,均可
泛型其实很常用!泛型约束(用于限制泛型的范围)也常用。
interface hasNameProperty {
name: string
}
//将某个对象的name属性的每个单词的首字母大小,然后将该对象返回
function nameToUpperCase<T extends hasNameProperty>(obj: T): T {
// obj.name//会报红色, 因为它并不知道T里面是否有name属性
//所以需要对T进行约束,起码是个对象,然后必须要有name属性 ---- >利用 泛型继承
obj.name = obj.name.split(' ').map(s => (s[0].toUpperCase() + s.substr(1))).join(' ');
return obj;
}
const o = {
name: 'wu jingwei',
age: 22,
gender: '男',
}
const nerO = nameToUpperCase(o);
console.log(nerO.name);//Wu Jingwei
多泛型
有可能依赖多个类型,那就多给几个泛型变量呗!
//将两个等长数组进行混合, [1,3,4] ['a', 'b', 'c'] => [1, 'a', 3, 'b', 4, 'c']
function mixinArray<T, P>(arr1: T[], arr2: P[]): (T | P)[] {
if (arr1.length !== arr2.length) {
throw new Error('两个数组长度不等');
}
const res: (T | P)[] = [];
for (let i = 0, len = arr1.length; i < len; i++) {
res.push(arr1[i]);
res.push(arr2[i]);
}
return res;
}
const result = mixinArray([1,3,4], ['a', 'b', 'c']);
console.log(result);
练习:自定义字典类
/**
*
开发一个字典类 Dictionary, 字典中会保存键值对的数据
键值对的特点:
* 键key:可以是任何类型,但不允许重复
值value:可以是任何类型
每个键唯一对应一个值
所有的键类型一致,所有的值类型也一致???不对吧..
字典类中对键值对数据的操作:
1. 设置某个键对应的值,若不存在则添加一个键值对
2. 给了键,删除对应的键值对
3. 循环每一个键值对
4. 得到当前键值对的数量
5. 判断某个键是否存在
*/
//dictionary.ts
export type Callback<T, P> = (key: T, val: P) => void;
export class Dictionary<K, V>{
private keys: K[] = [];
private values: V[] = [];
// private _size: number = 0;
set(key: K, val: V) {
const i = this.keys.indexOf(key);
if (i < 0) {
this.keys.push(key);
this.values.push(val);
} else {
this.values[i] = val;
}
}
forEach(callback: Callback<K, V>) {
this.keys.forEach((k, i) => {
const v = this.values[i];
callback(k, v);
});
}
has(key: K): boolean {
return this.keys.includes(key);
}
delete(key: K) {
const i = this.keys.indexOf(key);
if (i === -1) {
return;
} else {
this.keys.splice(i, 1);
this.values.splice(i, 1);
}
}
//关于size的访问器
get size(){
return this.keys.length;
}
}
//index.ts
import { Dictionary } from "./dictionary/dictionary";
const dic = new Dictionary<string, number>();
dic.set('234', 12354);
dic.set('123', 24355);
dic.set('nb', 666666);
dic.forEach((k,v)=>{
console.log(`${k}: ${v}`);
});
console.log(dic.has('ddd'));
console.log('准备删除键:123');
dic.delete('123');
console.log(dic);