升级Axios至1.7.7后TS报错:避免每次调用传Headers的方案
Axios升级到1.7.7后的TypeScript类型错误解决方案
问题描述
将Axios从0.28.0升级到1.7.7后,自定义Axios实例调用时必须重复传入headers字段,否则触发以下TypeScript错误:
TS2345: Argument of type '{ params: { accountType: string; }; }' is not assignable to parameter of type 'RequestConfig'. Property 'headers' is missing in type '{ params: { accountType: string; }; }' but required in type 'InternalAxiosRequestConfig<any>'.
原因分析
Axios 1.x版本中,InternalAxiosRequestConfig是内部处理请求时的专用类型,其中headers被标记为必填字段;而外部调用接口时应使用的AxiosRequestConfig类型中,headers是可选的。你自定义的RequestConfig错误继承了InternalAxiosRequestConfig,导致外部调用必须满足内部类型的必填约束,引发报错。
解决方案
方案1:移除自定义类型,直接使用Axios原生类型
删除自定义的RequestConfig和CustomHttpInstance类型定义,直接使用Axios提供的AxiosInstance类型,自动适配外部调用的AxiosRequestConfig(headers为可选字段):
修改后的核心代码:
import axios, { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse, } from 'axios'; import CryptoJS from 'crypto-js'; import { appConfig } from '../config/env'; const customHttpProvider: AxiosInstance = axios.create({ baseURL: appConfig.api.baseUrl, headers: { 'X-SIGN-TYPE': 2, 'X-API-KEY': appConfig.api.apiKey, 'Content-Type': 'application/json', }, }); customHttpProvider.interceptors.request.use(async (config: InternalAxiosRequestConfig) => { const timestamp = Date.now().toString(); const apiKey = appConfig.api.apiKey; const secretKey = appConfig.api.apiSecret; const recvWindow = 5000; let payload = ''; if (config.params && Object.keys(config.params).length) { payload = Object.keys(config.params) .map(key => `${key}=${encodeURIComponent(config.params[key])}`) .join('&'); } if (config.data) { payload = JSON.stringify(config.data); } const body = timestamp + apiKey + recvWindow + payload; const apiSignature = CryptoJS.algo.HMAC.create( CryptoJS.algo.SHA256, secretKey, ) .update(body) .finalize() .toString(CryptoJS.enc.Hex); config.headers['X-SIGN'] = apiSignature; config.headers['X-TIMESTAMP'] = timestamp; config.headers['X-RECV-WINDOW'] = recvWindow; return config; }); customHttpProvider.interceptors.response.use( (response) => response.data, (error) => Promise.reject(error.response?.data || error), ); export default customHttpProvider;
调用示例(无需重复传headers):
const getBalance = (accountType: string = 'UNIFIED') => { return customHttpProvider.get<{ retCode: number; retMsg: string; result: { memberId: string; accountType: string; balance: { coin: string; transferBalance: string; walletBalance: string; bonus: ''; }[]; }; retExtInfo: Record<string, any>; time: number; }>('/v5/asset/transfer/query-account-coins-balance', { params: { accountType, }, }); };
方案2:修正自定义RequestConfig类型,将headers设为可选
若需保留自定义类型结构,修改RequestConfig将headers改为可选字段:
import axios, { AxiosInstance, AxiosInterceptorManager, AxiosPromise, InternalAxiosRequestConfig, AxiosResponse, } from 'axios'; import CryptoJS from 'crypto-js'; import { appConfig } from '../config/env'; // 将headers字段改为可选 interface RequestConfig extends Omit<InternalAxiosRequestConfig, 'headers'> { headers?: InternalAxiosRequestConfig['headers']; } interface CustomHttpInstance extends AxiosInstance { (config: RequestConfig): AxiosPromise; (url: string, config?: RequestConfig): AxiosPromise; interceptors: { request: AxiosInterceptorManager<RequestConfig>; response: AxiosInterceptorManager<AxiosResponse>; }; getUri(config?: RequestConfig): string; request<T = any, R = T>(config: RequestConfig): Promise<R>; get<T = any, R = T>(url: string, config?: RequestConfig): Promise<R>; delete<T = any, R = T>(url: string, config?: RequestConfig): Promise<R>; head<T = any, R = T>(url: string, config?: RequestConfig): Promise<R>; options<T = any, R = T>(url: string, config?: RequestConfig): Promise<R>; post<T = any, R = T>( url: string, data?: any, config?: RequestConfig, ): Promise<R>; put<T = any, R = T>( url: string, data?: any, config?: RequestConfig, ): Promise<R>; patch<T = any, R = T>( url: string, data?: any, config?: RequestConfig, ): Promise<R>; } const customHttpProvider: CustomHttpInstance = axios.create({ baseURL: appConfig.api.baseUrl, headers: { 'X-SIGN-TYPE': 2, 'X-API-KEY': appConfig.api.apiKey, 'Content-Type': 'application/json', }, }) as CustomHttpInstance; // 拦截器逻辑保持不变 customHttpProvider.interceptors.request.use(async (config: RequestConfig) => { // ... 原有代码 return config; }); customHttpProvider.interceptors.response.use( (response) => response.data, (error) => Promise.reject(error.response?.data || error), ); export default customHttpProvider;
此方案既保留自定义类型,又允许调用时不强制传入headers。
内容的提问来源于stack exchange,提问作者rahulserver
相关产品推荐
相关产品推荐

