修改 axios response 返回类型

TypeScript 错误的类型提示

在 axios 相应拦截中修改了返回值:

service.interceptors.response.use(
  response => {
    return response.data
  }, 
  err => Promise.reject(err)
)

但是使用时发现返回值类型依然是 AxiosResponse<any>

const res = axios({})
type T = typeof res
// type T = AxiosPromise<any>

解决方案

解决方案参考:How to use Axios with TypeScript when using response interceptors (AxiosResponse issue)open in new window

1. 修改定义

这并不是一个好的方案,会影响到其他内容。

// src/types/axios/axios.d.ts
import axios from 'axios'

declare module 'axios' {
  export interface AxiosInstance {
    request<T = any> (config: AxiosRequestConfig): Promise<T>;
    get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
    delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
    head<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
    post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
    put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
    patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
  }
}
// tsconfig.json
{
  "compilerOptions": {
    "typeRoots": [
      "./node_modules/@types",
      "./src/types/",
    ]
  }
}

2. 指定返回类型

这是一个更好的选择,在 Allow custom return types(PR)open in new window 中:

export interface AxiosInstance {
  (config: AxiosRequestConfig): AxiosPromise;
  (url: string, config?: AxiosRequestConfig): AxiosPromise;
  defaults: AxiosRequestConfig;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
  request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
  get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
  put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
  patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
}

可以看到增加了 R 指定返回类型,当没手动制定时会根据 T 进行推导,这样就可以得到一个良好的解决方式:

interface User {
  name: string
  age: number
}

const res = axios.request<any, User>({})
type T = typeof res
// type T = Promise<User>

但作为接口而言,一般会加一层封装:

interface AxiosResponseData<T> {
  code: number
  message: string
  data?: T
}

interface User {
  name: string
  age: number
}

const res = axios.request<any, AxiosResponseData<User[]>>({})
type T = typeof res
// type T = Promise<AxiosResponseData<User[]>>

如果在响应拦截做了处理,则可以省略这一部分,毕竟每个 interface 都加这个还是有些麻烦的。