V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
zzlit
V2EX  ›  程序员

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

  •  
  •   zzlit · Feb 20, 2021 · 3261 views
    This topic created in 1895 days ago, the information mentioned may be changed or developed.

    小弟第一次用 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
        }
      });
    }
    
    Supplement 1  ·  Mar 1, 2021
    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 replies    2021-02-24 08:56:38 +08:00
    Pyrex23
        1
    Pyrex23  
       Feb 20, 2021 via iPhone   ❤️ 2
    第一个问题 前两天也遇到了类似的 最后用泛型解决了,可以参考一下这篇文章

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

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

    2. 没搞懂你为什么要这么做。能描述一下场景吗?
    zzlit
        5
    zzlit  
    OP
       Feb 20, 2021
    @Durandal01 你好~

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

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

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

    从我个人的观点来说,axios 也不可能添加一个它自己用不到的参数 :)
    myCupOfTea
        7
    myCupOfTea  
       Feb 24, 2021
    我直接 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;
    这两行我用不到 所以没有修改声明
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2582 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 15:28 · PVG 23:28 · LAX 08:28 · JFK 11:28
    ♥ Do have faith in what you're doing.