
Axios 1.x 的 TypeScript 实战指南模块解析、错误 Type Guard、请求泛型 D, P 与 Symbol 配置键【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios在 TypeScript 项目中接入 axios最常见的两个问题是模块解析配置导致默认导出报错和catch 块里error为unknown无法安全访问response、config等属性。本文基于 axios 仓库内的 TypeScript 官方指南结合 index.d.ts 类型定义与 lib/utils.js 合并逻辑源码系统讲解双模块格式下的moduleResolution配置要点、axios.isAxiosError/axios.isCancel两个 type guard 的窄化用法、AxiosRequestConfigD, P双泛型对请求体与查询参数的完整类型传递含paramsSerializer、适配器与response.config、AxiosInstance类型化实例与拦截器标注以及通过declare module扩充 Symbol 键自定义请求配置。读完本文你可以在 ESM/CJS 双环境下正确配置 tsconfig并对请求、响应、错误、取消全链路获得端到端的类型检查。类型定义文件的分发方式index.d.ts与index.d.ctsaxios 在 npm 包中直接携带 TypeScript 定义index.d.ts面向 ESMindex.d.cts面向 CJS因此两种模块格式下的类型检查与编辑器智能提示均开箱即用无需像某些库那样额外安装types/axios。从仓库根目录的 package.json 可以看到具体的分发机制当前仓库版本为 1.19.0顶层types: index.d.ts与typings: index.d.ts指向 ESM 定义文件exports[.].types按解析条件拆分require命中./index.d.ctsCommonJSdefault命中./index.d.tsESM。这正是双发布dual-publish的形态运行时 ESM 默认导出与 CJSmodule.exports并存类型文件也随之一分为二。理解这一点是后面所有模块解析配置建议的前提。模块解析Module Resolution配置注意事项由于 axios 同时以 ESM 默认导出和 CJSmodule.exports发布tsconfig 中需要做相应取舍推荐设置是moduleResolution: node16由module: node16隐式启用要求 TypeScript 4.7 或更高版本如果你的项目本身就是 ESM默认设置通常已经没有问题如果你把 TypeScript 编译为 CJS 且无法使用moduleResolution: node16必须启用esModuleInterop否则import axios from axios这类默认导入会因 CJS 端没有默认导出而报错如果你用 TypeScript 对 CJS 风格的 JavaScript 代码做类型检查checkJs场景唯一可行的选择就是moduleResolution: node16。一个典型的 tsconfig 配置示例{ compilerOptions: { module: node16, moduleResolution: node16, esModuleInterop: true, target: ES2020 } }要点在于moduleResolution决定了 TS 编译器走哪条exports分支去加载类型文件配置错时最典型的报错就是模块没有默认导出这类看似无解的类型错误——本质上是编译器解析到了与运行时不一致的入口。用 Type Guard 安全窄化 axios 错误axios.isAxiosError窄化unknown错误在catch块中error的默认类型是unknown或any直接访问error.response、error.config、error.code既不推荐也不安全。使用axios.isAxiosError这个 type guard 窄化之后你就可以在完整类型安全的前提下访问这些 axios 专属属性import axios from axios; let user: User | null null; try { const { data } await axios.get(/user?ID12345); user data.userDetails; } catch (error) { if (axios.isAxiosError(error)) { handleAxiosError(error); } else { handleUnexpectedError(error); } }其底层判定逻辑非常直接见 lib/helpers/isAxiosError.jsexport default function isAxiosError(payload) { return utils.isObject(payload) payload.isAxiosError true; }也就是说运行时该 guard 只是检查对象上的isAxiosError true标记位类型层面则由 index.d.ts 中AxiosError类声明的isAxiosError: boolean属性index.d.ts第 537 行配合 guard 的value is AxiosError签名完成窄化。AxiosError类声明index.d.ts第 524-539 行同时暴露了config、code、request、response、status、toJSON()等属性窄化之后这些字段全部可安全访问且类上还声明了ERR_NETWORK、ERR_BAD_RESPONSE、ERR_CANCELED等静态错误码常量便于在handleAxiosError中按error.code分支处理。axios.isCancelT()窄化取消错误到CanceledErrorT请求取消例如通过AbortSignal中断会以CanceledError形式抛出。用axios.isCancelT()可以把unknown错误窄化为CanceledErrorTconst controller new AbortController(); try { await axios.getUser(/user?ID12345, { signal: controller.signal }); } catch (error) { if (axios.isCancelUser(error)) { handleCancellation(error); } }类型签名定义在 index.d.ts 第 755 行export function isCancelT any, D any, P any(value: any): value is CanceledErrorT, D, P;注意该 guard 本身接受三个泛型参数——响应数据T之外还有请求数据D与查询参数P这与下面要讲的请求泛型体系是一脉相承的。运行时判断依据见 lib/cancel/isCancel.js检查value.__CANCEL__标记。请求数据与查询参数的类型化AxiosRequestConfigD, P双泛型AxiosRequestConfigD any, P any使用D表示请求体类型、P表示查询参数类型自定义参数序列化器paramsSerializer接收的也是同一个P。这一点可以从 index.d.ts 第 391-402 行的接口声明中得到印证export interface AxiosRequestConfigD any, P any { // ... params?: P; paramsSerializer?: | ParamsSerializerOptionsunknown extends P ? Recordstring, any : P | CustomParamsSerializerunknown extends P ? Recordstring, any : P; data?: D; // ... }unknown extends P ? Recordstring, any : P是一个条件类型技巧当你显式给出P时序列化器回调的参数就是P当P为默认的any时则退化为宽松的Recordstring, any从而保持向后兼容。完整示例import axios, { type AxiosPromise, type AxiosRequestConfig, type InternalAxiosRequestConfig, } from axios; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } interface SearchResponse { results: string[]; } const searchConfig: AxiosRequestConfigRequestBody, SearchParams { data: { includeArchived: false }, params: { query: axios, page: 1 }, paramsSerializer: (params) ${params.query}:${params.page ?? 1}, }; const response await axios.get(/search, searchConfig); response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined const invalidConfig: AxiosRequestConfigRequestBody, SearchParams { // ts-expect-error query 必须是字符串 params: { query: 123 }, };最后一段ts-expect-error演示了窄化收益query声明为string传入数字123会直接报错。泛型在整条链路上的传递默认请求结果会在response.config上保留D和P——即使请求别名方法是从带类型的请求配置中推断出这些类型的。从 index.d.ts 的类型声明看这条链路上的类型都携带参数泛型RawAxiosRequestConfigD, PAxiosRequestConfig的别名、InternalAxiosRequestConfigD, PAxiosDefaultsD, P、CreateAxiosDefaultsD, PAxiosResponseT, D, P、AxiosPromiseAxiosErrorT, D, P、CanceledErrorT, D, P可调用实例callable instances、适配器adapters以及mergeConfigD, P()声明在index.d.ts第 759 行。请求方法则以追加方式把P加为最后一个泛型——T, R, D, P——这样既有的响应数据T、自定义响应R、请求数据D的位置都不变显式提供的自定义响应类型仍然控制最终 resolve 的值P默认为any以保持向后兼容。适配器与显式类型化的 Promise一个显式标注类型的适配器可以完整保留两个请求泛型const searchAdapter ( config: InternalAxiosRequestConfigRequestBody, SearchParams ): AxiosPromiseSearchResponse, RequestBody, SearchParams Promise.resolve({ data: { results: [] }, status: 200, statusText: OK, headers: {}, config, }); declare const error: unknown; if (axios.isCancelSearchResponse, RequestBody, SearchParams(error)) { error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined }这里CanceledError的config字段类型为InternalAxiosRequestConfigD, P因此取消错误上也能追溯到完整的请求泛型——三个泛型参数顺序为T, D, P。类型化实例与拦截器把axios.create的结果标注为AxiosInstance把请求拦截器参数标注为InternalAxiosRequestConfig即可在一个自定义客户端上获得端到端的类型检查import axios, { AxiosInstance, InternalAxiosRequestConfig } from axios; const apiClient: AxiosInstance axios.create({ baseURL: https://api.example.com, timeout: 10000, }); apiClient.interceptors.request.use((config: InternalAxiosRequestConfig) { // 添加认证 token、记录日志等 return config; });类型层面AxiosInstance继承自Axiosindex.d.ts第 710 行并额外提供调用签名因此既能apiClient(/users)也能apiClient.getUser(/users/1)。而InternalAxiosRequestConfig与AxiosRequestConfig的关键区别在index.d.ts第 485-487 行export interface InternalAxiosRequestConfigD any, P any extends AxiosRequestConfigD, P { headers: AxiosRequestHeaders; }进入请求管道后headers已经是确定的AxiosHeaders对象必填且不再是原始头对象或 AxiosHeaders 的联合所以拦截器内可以放心使用config.headers.set(...)这样的对象方法而不必再做判空或形态判断。使用 Symbol 键扩展自定义请求配置axios 在合并默认配置与单次请求配置时会保留自身的、可枚举的symbol 属性。因此应用可以通过模块增强module augmentation给AxiosRequestConfig添加一个特定的 symbol 键并在拦截器或适配器中从InternalAxiosRequestConfig读取该选项import axios from axios; export const someFlag: unique symbol Symbol( some flag used in request interceptor ); declare module axios { interface AxiosRequestConfigD any, P any { [someFlag]?: boolean; } } axios.interceptors.request.use((config) { if (config[someFlag]) { config.headers.set(X-Some-Flag, enabled); } return config; }); await axios.get(/users, { [someFlag]: true });这套机制之所以成立取决于合并逻辑对 symbol 键的处理。在 lib/utils.js 的merge函数中可以看到第 614-626 行附近先通过forEach(source, assignValue)遍历字符串键随后显式执行Object.getOwnPropertySymbols(source)且仅当propertyIsEnumerable.call(source, symbol)为真时才执行assignValue(source[symbol], symbol)——即只有自身的、可枚举的 symbol 属性会被复制非枚举或继承来的 symbol 属性不会。同时第 590-592 行的注释也说明了 caseless大小写不敏感查找仅适用于字符串键symbol 键按身份identity精确匹配不会发生键名折叠。这个能力适合实现一些不想污染公共配置字段的内部标记位如重试标记、灰度开关并且类型系统可以通过declare module axios增强保持完整检查。响应数据的类型化Axios 的请求方法对响应数据类型是泛型的向axios.getT以及其他别名方法传入类型参数即可给response.data赋予精确类型interface User { id: number; name: string; } const { data } await apiClient.getUser(/users/1); // data 的类型为 User这一点与前面axios.getUser(/user?ID12345, { signal })的取消示例中T的位置一致——T始终控制响应数据也就是 Promise 的 resolve 值除非你通过R显式指定了自定义响应类型。小结与核对清单场景正确做法依据ESM 项目保持默认即可推荐moduleResolution: node16TS ≥ 4.7docs/es/pages/advanced/type-script.md编译到 CJS 且不能用 node16必须启用esModuleInterop同上对 CJS JavaScript 做类型检查只能用moduleResolution: node16同上catch 块处理 HTTP/网络错误axios.isAxiosError(error)窄化后访问response/config/codelib/helpers/isAxiosError.js处理请求取消axios.isCancelT(error)窄化为CanceledErrorTlib/cancel/isCancel.js、index.d.ts类型化请求体/查询参数AxiosRequestConfigD, P请求方法泛型为T, R, D, Pindex.d.ts自定义客户端AxiosInstance 拦截器标注InternalAxiosRequestConfigindex.d.ts内部标记位配置declare module axios增强 symbol 键仅自身可枚举 symbol 会被合并保留lib/utils.js掌握以上内容后你的 TypeScript axios 项目从 tsconfig 配置到请求、响应、错误、取消的全链路都能获得静态检查ts-expect-error可以成为回归类型约束的轻量测试手段。【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考