TypeScript泛型

前言

  之前上手typescript+vue的时候,vue也没有完全支持typescript,当时看typescript的文档的时候,就大概的过了一遍。当时也不是很清楚如何在vue中使用typescript,更别说泛型了。

  软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。

  这是typescript官方文档的介绍,个人感觉不是太好理解,个人理解这里的组件更多的是指方法,官方的泛型示例也都是跟方法相关的。

泛型

// 无返回值
function doSomething(obj: any): void {
    // do something
}
// 有返回值
function getSomething(obj: any): any {
    return obj;
}

  我在vue中就只使用了这两种,说起来也是尴尬,typescript的真正功力都没发挥出来,把typescript写成了javascript,还多堆了两个关键词的代码。其实之前在写的时候就有发现所有具有返回值的方法都指定any有何意义呢。

  typescript的基础类型也很多,如果一个方法接受一个参数直接进行返回,如果要用上强类型,是否需要写多个方法,仅仅是指定类型的不同,答案是否定的。一个方法能够接受任意类型的参数并返回相应类型的参数,就是泛型。泛型方法:

function identity<T>(arg: T): T {
    return T;
}

identity方法就是泛型,它接受任意类型的参数,并可以对参数类型进行捕获,并将捕获的类型返回。这样的identity可以适用多个类型。定义了泛型方法之后,有两种方法可以进行使用,一种是传入参数和类型,第二种是使用类型推论:

// 传入参数和类型 T就是string
let out = identity<string>("string"); // type: string
//类型推论 T会根据参数类型推断为number
let out = identity<number>(10); // type: number

  使用泛型变量时需要注意,如果不具体指定传入参数的具体类型如数组、对象,就直接使用数组或对象的方法会报错。因为类型变量代表的是任意类型,传入的参数可能是数字、布尔值等,此时我们要使用length查看其长度是不行的,因为都不具有length属性。如果是数组,需要在泛型的基础上指定为数组。

function identity<T>(arg: T[]): T[] {
    return arg.length;    // ok
}
// or
function identity<T>(arg: Array<T>): Array<T> {
    return arg.length; // ok
}

创建泛型

  在普通方法声明的基础上加一个类型参数,这里的参数名T可以是任意的,而且使用时只要数量对的上就行。

function identity<T>(arg: T): T {
    // do something
}

  使用对象字面量进行创建,在此基础上也可以抽象成接口:

let myIdentity: {<T>(arg: T): T} = identity;
// or
interface GenericFunction {
    <T>(arg: T): T;
}
let myIdentity: GenericFunction = identity;

使用

  主要在使用上,如果一个方法需要适配不同类型的传入参数,就可以使用泛型定义方法。这种使用方式就如identity方法一样,根据传入参数不同返回指定类型的参数。

  另外就是指定请求参数类型。如axios请求:

    http.post<T>(`${url}`, params)

  还有就是定义请求返回数据的类型,这样我们就可以在ide中使用.运算符拿到response中的属性。

export interface LoginRequest {
    username: string;
    password: string;
}

export interface LoginPayload {
    username: string;
    token: string;
    ···
}

export const Login = async (loginParams: LoginRequest): Promise<LoginPayload> => {
    try {
        const response = await http.post<LoginRequest>('/login', loginParams);
        return response.data as LoginPayload;
    } catch (e) {
        throw(e);
    }
}

  代码中定义了两个接口,分别为登陆的请求参数类型,和登陆返回值类型。在return的时候使用as断言。这样在请求得到的返回结果,在ide中就可以使用.得到返回结果中的token等属性,比较方便,不需要将请求结果打印出来,或者查看请求结果。

总结

   除此之外还有泛型类和类类型,于方法类似,只不过是换成了类的写法,而且不常用。对于学习新的语言或者框架使用粗略的过一遍文档,然后就上手做,肯定开始很多东西都不了解。就像我之前写的,感觉完全没有用上typescript的强类型,把ts写成了js。不过这种学习的方式好的一点是,在了解过后,写了一段时间代码,也踩过一些坑之后,再次学习的话,就有醍醐灌顶、豁然开朗的感觉,记忆也会更加深刻。了解清楚了泛型的使用,现在也可以去更正之前代码的错误,加上泛型或指定其类型。