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