使用TS泛型优雅地调用axios

2019.03.03

问题场景

在平时的开发中我们经常会调用服务端的api来完成我们的业务需求,通常我们会把请求封装成promise的形式便于我们调用。代码如下:

import request from '@/uitls/request';
request(url, config).then(data=>{
  console.log(data);
})

都2021年了,TS已经很流行了,在TS环境下,上述的代码是有问的,then里的data参数类型为unknown,这就会给我们后续的开发带来困扰。因为我们在封装request函数的时候并没有定义这个值,当然我们也没法定义它,因为它是不确定的。

image

解决方案

用类型断言暴力解决问题

import request from '@/uitls/request';
type User = {
  name: string;
  age: number;
}
request(url, config).then(data=>{
  const {name, age} = data as User
})

这种暴力的手段固然可以解决问题,但是类型断言在ts里毕竟是不推荐的。

用泛型的方式来进行类型的推断

import request from '@/uitls/request';
type User = {
  name: string;
  age: number;
}
request<User>(url, config).then(data=>{
  const {name, age} = data;
})

我们希望通过传入类型的方式,最后推断出返回的data类型是我们期望的类型,这是比较完美的一种方案。

通过axios来实现泛型函数

axios 的api方法,本身就支持泛型的调用, 它的实例类型定义如下:

export interface AxiosInstance {
  //....
  request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
  get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  //....
}

因此我们利用axios方法的特性编写支持方向的request函数,是非常方便的。

import Ax from 'axios';
export function request<T>() {
  return Ax.get<T>('/somepath')
    .then(res => {
      const result = res.data;
      if (!result.success) {
        throw 'error'
      }
      return result;
    })
    .catch(err => console.error(err));
}
request<User>().then(data=>data);

上面的方式还存在一点小问题,Ax.get里返回的res指的是服务端返回的完整的数据类型,而我们reqest方法的then回调里返回的数据只是服务端返回的数据的某个字段的类型。因此还需要对上述的方法做个改造。

// 定义一个泛型接口,代表服务端的数据类型
interface ResponseData<T = any> {
  code: string;// 状态码
  data: T;// 具体的业务数据类型
  msg: string; // 错误信息
  success: boolean; // 是否成功
}

import Ax from 'axios';
export function request<T>() {
  return Ax.get<ResponseData<T>>('/somepath')
    .then(res => {
      const result = res.data;
      if (!result.success) {
        throw 'error'
      }
      // data里面的数据类型才是我们所关心的
      return result.data;
    })
    .catch(err => console.error(err));
}
request<User>().then(data=>data);

通过这种方式我们就实现了,给方法传入我们想要的类型,它就会帮我们推断出返回值的具体类型。有了类型,当然后面编码就轻松多了,真的是爽!