10. 泛型
于2023-10-06 15:40:43发布
87
1、泛型的作用
先看这样一个需求
定义一个getId方法,传入一个值,返回这个值。(注意,传入的值可以是任意类型)
任意类型的话,只能想到把传入的参数类型写成any了,如下 :
function getId(val:any) {
return val;
}
const res = getId(123);
但是这样也会带来新的问题,返回值res的类型是any,在编译器中没有提示了。
那有没有一种解决方案,比如我传入的参数是number类型,则val就是number类型。我传入的参数是string类型,val就是string类型呢?如下:
function getId(val:number) {
return val;
}
const res = getId(123); // getId传入的参数是123,则val是number类型
function getId(val:string) {
return val;
}
const res = getId('456'); // getId传入的参数是'456',则val是string类型
泛型就可以实现上面的逻辑,val的类型是动态的。传入的参数如果类型是number,则val的类型是number,传入的是string,则类型是string。泛型的使用案例如下 :
function getId<T>(val:T): T {
return val;
}
const res = getId<number>(123);
在编译器中,上面的代码是有提示的
2、简化泛型的声明与调用
上面的getId例子,泛型的声明与调用可以做进一步的简化。如下 :
function getId<T>(val:T) {
return val;
}
const res = getId(123);
tips:泛型不一定要用T来表示,用什么字母都可以。比如 :
function getId<A>(val:A) {
return val;
}
const res = getId(123);
3、泛型添加约束
先看这么一个案例
function getId<T>(val:T) {
console.log(val.length) // 这里报错 Property 'length' does not exist on type 'T'
return val;
}
getId是一个泛型函数,参数val也是泛型类型,所以编译器是不能确定,你以后传入的参数是不是一定有length属性,如 :
getId('123') // ok
getId(456) // 错误,123是number类型,没有length属性
解决方法如下 :(给泛型函数T继承一个接口)
interface Ilength {
length: number
}
function getId<T extends Ilength>(val:T) {
console.log(val.length)
return val;
}
getId('123'); // ok
getId(456); // 编译器报错,Argument of type 'number' is not assignable to parameter of type 'Ilength'
4、定义多个泛型keyof
先看这么一个案例
定义一个函数,传入一个对象,在传入一个字符串属性名,返回属性值
function getPro<T, K extends keyof T>(obj:T, key:K) {
return obj[key];
}
const obj = {
name: 'tom',
age: 18
}
const res1 = getPro(obj, 'name'); // ok
const res2 = getPro(obj, 'hobby'); // 编译器报错,obj身上并没有hobby属性
console.log(res1);
console.log(res2);
该用法具体视频: https://www.bilibili.com/video/BV1vX4y1s776?p=29&vd_source=1c1f36c373bf5f183bb470989dc9a264
5、泛型接口
interface Person <T> {
name: T, // name是任意类型,具体是什么类型,则是在声明变量的时候定义
age: number,
hobby: T[] // hobby是一个数组,里面装的数据是任意类型,具体是什么类型,则是在声明变量的时候定义
}
const p: Person<string> = { // 决定Person的T类型是string类型
name: 'tom', // name必须是string类型
age: 18,
hobby: ['1','2','3'] // hobby里面的数据必须是string类型
}
6、处理接口返回数据类型的例子
interface IResponse <T> {
code :number,
data: T,
msg: string
}
interface IRecordsList <T> {
currentPage: number,
pageSize: number,
pages: number,
total: number,
records: T
}
interface IRecordsData {
id: string,
name: string,
age: number
}
const res:IResponse<IRecordsList<IRecordsData []>> = {
code : 200,
data: {
currentPage: 1,
pageSize: 20,
pages: 1,
total: 2,
records: [{
id: '1001',
name: 'tom',
age: 25
},{
id: '1002',
name: 'marry',
age: 12
},]
},
msg: 'Success',
}
开发中,IResponse、IRecordsList可以写到一个文件里面去export出来,因为多个页面都会用到,不同的只有IRecordsData