V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zzlit
V2EX  ›  程序员

请求几个关于 ts 封装 axios 的问题

  •  
  •   zzlit · 2021-02-20 12:25:32 +08:00 · 2592 次点击
    这是一个创建于 1379 天前的主题,其中的信息可能已经有所发展或是发生改变。

    小弟第一次用 ts 写项目,之前都是写一些 demo,下面有几个关于 axios 封装的问题,请求大佬们指点一下,感激不尽!

    第一个问题是我在响应拦截里面做了一下处理,因为用的时候有的需要返回 code 以应对不同的情况,有的不需要直接用 data,就导致了不好定义req这个方法返回的类型,在使用的时候res.出来的是 axios 定义的 AxiosResponse 里面的东西,我想让它提示我返回的 data 里面自己定义的类型 或者是 没有 data 时候的 boolean?

    // 后端返回的格式
    {
      code: 0,
      data: xxx,
      errorMsg: xxx
    }
    
    // 使用的时候
    const req = async (params: any) => {
      const res = await post(url, params);
      /** res.出来的提示是 AxiosResponse<any>的这些东西,axios 声明的如下
        export interface AxiosResponse<T = any>  {
          data: T;
          status: number;
          statusText: string;
          headers: any;
          config: AxiosRequestConfig;
          request?: any;
        }
      */
    };
    
    request.interceptors.request.use(
      config => {
        const headers = config.headers;
        const { extraConfig } = headers;
        if (extraConfig) {
          const { needLoading, needToken } = extraConfig;
          if (needLoading) // loading
          if (needToken) // token
        }
        return config;
      },
      error => {
        return Promise.reject(error);
      }
    );
    request.interceptors.response.use(
      response => {
        if (response.config.headers.extraConfig.needHandleError) {
          // 需要返回 code 以应对不同的情况
          return Promise.resolve(response.data);
        }
        const { data, errorMsg, code } = response.data;
        if (code === '00000') {
          if (data === null || data === undefined) {
            return Promise.resolve(true);
          } else {
            return Promise.resolve(data);
          }
        } else {
          return Promise.reject(new Error(errorMsg || 'Error'));
        }
      },
      error => {
        return Promise.reject(error);
      }
    );
    

    第二个问题就是我要配置一些自定义的内容进去,直接写在request的参数里面不行,因为AxiosRequestConfig没有定义这个额外的配置参数所以我的做法是写在了headers里面,这样就导致了请求的时候网页上也会看到我写的这个extraConfig: [object, object],我是不想看到这个的,我想到的一个是不是重新定义AxiosRequestConfig?这个该怎么写呢?

    interface Config {
      needLoading?: boolean;
      needToken?: boolean;
      needHandleError?: boolean;
    }
    
    const dealConfig = (extraConfig?: Config) => {
      const defaultExtraConfig = {
        needLoading: true,
        needToken: true,
        needHandleError: false
      };
      if (typeof extraConfig === 'undefined') {
        extraConfig = defaultExtraConfig;
      } else {
        Object.keys(defaultExtraConfig).forEach(key => {
          if (!Object.prototype.hasOwnProperty.call(extraConfig, key)) {
            (extraConfig as Config)[key] = defaultExtraConfig[key];
          }
        });
      }
      return extraConfig;
    };
    
    export function post(url: string, params: object, extraConfig?: Config) {
      extraConfig = dealConfig(extraConfig);
      return request({
        url,
        method: 'post',
        data: params,
        headers: {
          extraConfig
        }
      });
    }
    
    第 1 条附言  ·  2021-03-01 17:26:38 +08:00
    Durandal01 大佬指点了一下 `axios.post<DataType, DataType>() 第二个泛型参数就是实际返回的类型。`,我才发现原来之前这种写法 axios({method: 'post'})的是没有的,需要用`axios.post()`,这里才能定义两个参数,例如 get:
    ```
    get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;

    ```

    但是实际上如果用的时候还是返回了 AxiosResponse 声明的东西,但的确是定义了参数,所以就从 Pyrex23 大佬提供的博文还是重新声明了一下
    ```
    get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
    ```

    这样返回的就不是 AxiosResponse 声明的东西而是自己定义的了,所以我在用的时候下面这样

    ```
    interface ApiResponse<T, R> {
    (param: T): Promise<R>;
    }

    const apiMethod: ApiResponse<参数类型, 返回类型> = params => {
    return post<返回类型>(HOSPITALBASE.UPLOAD_DEVICE_ID, params);
    };
    ```
    这样就可以在用 apiMethod 这个方法的时候自动推导出定义的返回类型里面的内容了,如果有能更好的处理方法后面再更新,第二个问题还没有找到好的处理办法。
    7 条回复    2021-02-24 08:56:38 +08:00
    Pyrex23
        1
    Pyrex23  
       2021-02-20 12:34:09 +08:00 via iPhone   ❤️ 2
    第一个问题 前两天也遇到了类似的 最后用泛型解决了,可以参考一下这篇文章

    https://gaojiajun.cn/2019/12/typescript-axios-interceptor-commondata/
    zzlit
        2
    zzlit  
    OP
       2021-02-20 13:42:38 +08:00
    @Pyrex23 我也看到了这篇,处理方法就不是我种在成功的时候就已经处理完 code 把 data 返回了,是在使用的时候去处理 code,其实我看了 github 上几个项目基本都是这样处理的,的确比我目前这种要好一点,作为了一个我的主要备选方案了
    zzlit
        3
    zzlit  
    OP
       2021-02-20 13:46:40 +08:00
    而且我下面的说的重新声明 AxiosRequestConfig 就是看到这边重新声明了一个 AxiosInstance 感觉是可以实现作为参数加到请求去而不是放在 headers 里面
    Durandal01
        4
    Durandal01  
       2021-02-20 15:33:11 +08:00
    @zzlit

    1. 以 post 为例: axios.post<DataType, DataType>() 第二个泛型参数就是实际返回的类型。

    2. 没搞懂你为什么要这么做。能描述一下场景吗?
    zzlit
        5
    zzlit  
    OP
       2021-02-20 16:52:50 +08:00
    @Durandal01 你好~

    首先第一点:post 的例子的第二个泛型参数这是上面提到的文章里面我看到重新对 AxiosInstance 声明才有的吧?原来的话只有只一个的泛型参数的;

    然后问题二的场景是:我有一些请求接口需要 loading,有一些需要 token,有一些又需要返回带 code 的参数(因为我现在的处理是把 code 统一处理了,成功的返回就是直接的 data ),所以我就定义了一个 extraconfig 参数想在请求接口的时候带进来,然后在请求拦截器和响应拦截器里面都需要去做一些操作,场景就是这样的。
    Durandal01
        6
    Durandal01  
       2021-02-20 20:30:16 +08:00   ❤️ 1
    @zzlit
    https://github.com/axios/axios/blob/master/index.d.ts#L140-L146
    看这里,并不需要重新声明 instance ;

    第二个可以考虑下通过 path 或者别的什么预先声明 interceptor 的处理函数,这样就不依赖外部传入的参数了。如果一定要采用命令式的方式,可以考虑下 transferRequest 和 transferResponse 参数。

    从我个人的观点来说,axios 也不可能添加一个它自己用不到的参数 :)
    myCupOfTea
        7
    myCupOfTea  
       2021-02-24 08:56:38 +08:00
    我直接 as 重新声明了下类型


    ```ts
    type MyResponse<T = any> = T;

    const request = axios.create({
    baseURL: `/${apiPrefix || ''}`,
    }) as {
    (config: AxiosRequestConfig): AxiosPromise;
    (url: string, config?: AxiosRequestConfig): AxiosPromise;
    defaults: AxiosRequestConfig;
    interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<MyResponse>;
    };
    getUri(config?: AxiosRequestConfig): string;
    request<T = ReqResponse, R = MyResponse<T>>(
    config: AxiosRequestConfig,
    ): Promise<R>;
    get<T = ReqResponse, R = MyResponse<T>>(
    url: string,
    config?: AxiosRequestConfig,
    ): Promise<R>;
    delete<T = ReqResponse, R = MyResponse<T>>(
    url: string,
    config?: AxiosRequestConfig,
    ): Promise<R>;
    head<T = ReqResponse, R = MyResponse<T>>(
    url: string,
    config?: AxiosRequestConfig,
    ): Promise<R>;
    options<T = ReqResponse, R = MyResponse<T>>(
    url: string,
    config?: AxiosRequestConfig,
    ): Promise<R>;
    post<T = ReqResponse, R = MyResponse<T>>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
    ): Promise<R>;
    put<T = ReqResponse, R = MyResponse<T>>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
    ): Promise<R>;
    patch<T = ReqResponse, R = MyResponse<T>>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
    ): Promise<R>;
    };

    ```

    (config: AxiosRequestConfig): AxiosPromise;
    (url: string, config?: AxiosRequestConfig): AxiosPromise;
    这两行我用不到 所以没有修改声明
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2847 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 06:20 · PVG 14:20 · LAX 22:20 · JFK 01:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.