
axios TypeScript 类型系统实战泛型请求、类型化实例、类型守卫与 ESM/CJS 模块解析配置【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axiosaxios 原生内置 TypeScript 类型定义无需依赖types/axios第三方包。本文以 axios 仓库中“TypeScript example”文档为主线完整覆盖类型导入、泛型请求标注、函数/POST 请求类型封装、类型化实例与拦截器、错误类型收窄等核心操作并结合index.d.ts、package.json与lib/源码剖析背后的类型体系与双格式ESM/CJS分发机制帮助你在 TypeScript 项目中获得端到端的类型安全。原生内置类型无需安装 types 包axios 的类型定义随 npm 包一起发布通过 index.d.tsESM 入口和 index.d.ctsCJS 入口两套声明文件分别服务两种模块格式。从 package.json 的exports字段可以看到分发规则exports: { .: { types: { require: ./index.d.cts, default: ./index.d.ts }, ... default: { require: ./dist/node/axios.cjs, default: ./index.js } } }也就是说用import引入时会拿到 ESM 入口./index.js与类型文件index.d.ts用require引入时会拿到 CJS 构建产物./dist/node/axios.cjs与类型文件index.d.cts。这就是文档中强调“axios dual-publishes ESM and CJS双格式发布”的出处也是后文 tsconfig 注意事项的根源。另外 index.d.ts 第一行声明了// TypeScript Version: 4.7与文档中“推荐配置要求 TypeScript 4.7 或更高版本”的说法相互印证。导入类型Importing typesaxios 把所有核心类型从包根路径导出你可以按需直接导入import axios from axios; import type { AxiosRequestConfig, AxiosResponse, AxiosError } from axios;这些类型在 index.d.ts 中均有完整声明其中与日常使用最相关的几个包括类型定义位置用途AxiosRequestConfigD, Pindex.d.ts请求配置D为请求体类型、P为查询参数类型InternalAxiosRequestConfigD, Pindex.d.ts拦截器中拿到的配置headers为必填的AxiosRequestHeadersAxiosResponseT, D, H, Pindex.d.ts响应对象T即response.data的类型AxiosErrorT, D, Pindex.d.ts错误对象携带response、config、code等属性AxiosInstanceindex.d.tsaxios.create()的返回类型可调用接口AxiosStaticindex.d.ts默认导出axios对象本身的类型值得注意的是InternalAxiosRequestConfig的定义export interface InternalAxiosRequestConfigD any, P any extends AxiosRequestConfigD, P { headers: AxiosRequestHeaders; }它继承自AxiosRequestConfig但把headers从可选RawAxiosRequestHeaders MethodsHeaders | AxiosHeaders收紧为必填的AxiosRequestHeaders。原因是进入请求拦截器时axios 已经完成默认 headers 与请求 headers 的合并此时 headers 一定是一个可用的AxiosHeaders对象——这个设计直接决定了下文拦截器的写法。为请求标注类型Typing a request在请求方法上使用泛型参数即可告诉 TypeScript 响应数据的形状import axios from axios; type Post { userId: number; id: number; title: string; body: string; }; const response await axios.getPost(https://jsonplaceholder.typicode.com/posts/1); console.log(response.data.title); // TypeScript knows this is a string对应到类型声明get的签名为getT any, R AxiosResponseDefault, D any, P any( url: string, config?: AxiosRequestConfigD, P ): PromiseAxiosResponseResultT, R, D, P;见 index.d.ts四个泛型的职责分别是Tresponse.data的类型即上文示例中的PostR自定义整个响应形状默认AxiosResponseDefault唯一符号标记解析为标准的AxiosResponseT, D, {}, PD请求体类型P查询参数params的类型默认any。因此axios.getPost(...)等价于声明PromiseAxiosResponsePost, any, {}, anyresponse.data被精确推断为Postresponse.data.title是string而非any。delete、head、options、post、put、patch、postForm等所有请求方法都遵循相同的T, R, D, P泛型模式见 index.d.ts用法一致。为函数标注类型Typing a function把请求封装进显式返回类型的函数可以最大化类型安全性import axios, { AxiosResponse } from axios; type Post { userId: number; id: number; title: string; body: string; }; const getPost async (id: number): PromisePost { const response await axios.getPost( https://jsonplaceholder.typicode.com/posts/${id} ); return response.data; };这里的类型链路是axios.getPost返回AxiosResponsePostresponse.data即Post函数签名PromisePost与之吻合。调用方因此拿到的是干净的领域类型Post而不是包裹了status/headers/config的完整响应对象——把“响应壳”留在函数内部把“数据”暴露给调用方是 axios 类型化封装的常见做法。为 POST 请求标注类型Typing a POST requestPOST 场景下可以同时标注请求体与期望响应type CreatePostBody { title: string; body: string; userId: number; }; type CreatePostResponse CreatePostBody { id: number }; const createPost async (data: CreatePostBody): PromiseCreatePostResponse { const response await axios.postCreatePostResponse( https://jsonplaceholder.typicode.com/posts, data ); return response.data; };post方法的签名为postT, R, D, P(url, data?: D, config?)见 index.d.ts。如果希望请求体data也参与类型推导而非any可以在泛型第三个位置传入或给config显式标注AxiosRequestConfigCreatePostBody, ...——因为AxiosRequestConfig的data?: D字段与请求方法共享D这个类型槽位见 index.d.ts。此外paramsSerializer也会接收与P一致的参数类型可参考仓库英文文档 TypeScript 页 中 “Typing request data and query params” 一节的完整示例。类型化的 axios 实例Typed axios instance创建类型化实例可以把baseURL、timeout、默认 headers 从第一步就固化为类型的一部分import axios from axios; import type { AxiosInstance } from axios; const api: AxiosInstance axios.create({ baseURL: https://api.example.com, timeout: 5000, });axios.create的返回类型是AxiosInstance它extends Axios并额外声明了可调用签名见 index.d.tsexport interface AxiosInstance extends Axios { T any, R AxiosResponseDefault, D any, P any( config: AxiosRequestConfigD, P ): PromiseAxiosResponseResultT, R, D, P; T any, R AxiosResponseDefault, D any, P any( url: string, config?: AxiosRequestConfigD, P ): PromiseAxiosResponseResultT, R, D, P; create(config?: CreateAxiosDefaults): AxiosInstance; ... }这意味着api(url, config)、api.getT(url)、api.postT, R, D(url, data)等所有调用形式都受类型检查且实例可以链式create出携带更多默认值的子实例。从源码看axios.create的实现在 lib/axios.js 中Axios.prototype.create function create(instanceConfig) { const mergeConfig (config1, config2) mergeConfigWithContext(...); // ... const instance bind(Axios.prototype.request, context); // ... return createInstance(mergeConfig(defaultConfig, instanceConfig)); };即新实例 默认配置与传入配置经mergeConfig合并后通过createInstance生成默认导出axios本身也是createInstance(defaults)的结果见 lib/axios.js。这也解释了为什么实例与axios静态对象拥有相同的 API 面二者是同一Axios类的不同实例。另外 lib/axios.js 中axios.default axios;这一行是为 CJSesModuleInterop场景下的默认导入兜底与下文编译器配置注意事项直接相关。类型化的拦截器Typed interceptorsv1.x 中请求拦截器应使用InternalAxiosRequestConfig而不是AxiosRequestConfigimport axios from axios; import type { InternalAxiosRequestConfig, AxiosResponse } from axios; api.interceptors.request.use((config: InternalAxiosRequestConfig) { config.headers.set(Authorization, Bearer ${getToken()}); return config; }); api.interceptors.response.use( (response: AxiosResponse) response, (error) Promise.reject(error) );为什么必须区分这两个类型从类型声明看AxiosInterceptorManager按场景分发不同的use签名见 index.d.tstype AxiosRequestInterceptorUseT ( onFulfilled?: AxiosInterceptorFulfilledT | null, onRejected?: AxiosInterceptorRejected | null, options?: AxiosInterceptorOptions ) number;请求拦截器的T固定为InternalAxiosRequestConfig响应拦截器的T固定为AxiosResponse。如果你误用AxiosRequestConfig标注请求拦截器参数config.headers.set(...)会编译报错——因为AxiosRequestConfig.headers是可选的且可能是RawAxiosRequestHeaders这类尚未初始化的原始对象而InternalAxiosRequestConfig.headers是必填的AxiosRequestHeaders即AxiosHeaders实例set、get、normalize等方法见 index.d.ts 中的AxiosHeaders类声明才能被正确调用。拦截器的运行时行为可在 lib/core/InterceptorManager.js 中印证use(fulfilled, rejected, options)注册处理器并返回递增的ideject(id)移除指定处理器clear()清空整个栈options还支持synchronous与runWhen两个选项与类型声明中的AxiosInterceptorOptions一一对应。为错误标注类型Typing errors使用axios.isAxiosError()类型守卫收窄捕获到的错误import axios, { AxiosError } from axios; type ApiError { message: string; code: number; }; try { await axios.get(/api/protected-resource); } catch (error) { if (axios.isAxiosErrorApiError(error)) { // error.response?.data is typed as ApiError console.error(error.response?.data.message); console.error(error.response?.status); } else { throw error; } }isAxiosError在类型层面的签名是标准守卫见 index.d.tsexport function isAxiosErrorT any, D any, P any( payload: any ): payload is AxiosErrorT, D, P;传入业务错误类型ApiError作为T后收窄得到的AxiosErrorApiError中response?.data即为ApiErrorresponse?.status为number | undefined。运行时对应的实现挂载在默认导出上axios.isAxiosError isAxiosError见 lib/axios.js检查逻辑见 lib/helpers/isAxiosError.js。AxiosError类本身携带丰富的类型化静态错误码便于按error.code精确分支处理见 index.d.tsstatic readonly ERR_NETWORK ERR_NETWORK; static readonly ERR_BAD_RESPONSE ERR_BAD_RESPONSE; static readonly ERR_CANCELED ERR_CANCELED; static readonly ECONNABORTED ECONNABORTED; static readonly ETIMEDOUT ETIMEDOUT; // ...此外取消错误可以用配套的axios.isCancelT()守卫收窄为CanceledErrorT类型声明见 index.d.ts 与 index.d.ts配合AbortController使用更多模式可参考 TypeScript 文档页。TypeScript 编译器配置注意事项由于 axios 以 ESM 为默认导出、CJS 为module.exports双格式发布不同 tsconfig 下存在一些注意点与 TypeScript 文档页 中 “Module resolution caveats” 的说明一致推荐moduleResolution: node16由module: node16隐含要求 TypeScript 4.7 及以上。这是与index.d.ts头部TypeScript Version: 4.7声明匹配的配置。若使用 ESM 编译默认设置通常即可。若编译目标为 CJS 且无法使用moduleResolution: node16必须启用esModuleInterop: true。这对应 CJS 端axios.default axios的兜底设计lib/axios.js开启esModuleInterop后import axios from axios在 CJS 编译下能正确取到module.exports.default上的完整对象。若使用 TypeScript 对 CJS JavaScript 代码做类型检查checkJs唯一选择是moduleResolution: node16。这些并非纸面约定仓库自带针对两种模块格式的类型兼容性测试。CJS 侧的 tests/module/cjs/tests/typings.module.test.cjs 与 ESM 侧的 tests/module/esm/tests/typings.module.test.js 都创建临时工程并以如下配置运行tsc --noEmitconst tsconfig { compilerOptions: { checkJs: true, module: node16, }, };其中 CJS 侧还会专门验证isCancel收窄到CanceledError的类型行为cjs-is-cancel-typing.ts夹具与上文“为错误标注类型”一节的做法互为印证。小结axios 通过index.d.tsindex.d.cts双类型文件配合exports映射为 ESM/CJS 两种消费方式提供开箱即用的类型支持最低 TypeScript 4.7请求方法统一采用T, R, D, P泛型模式axios.getPost(...)即可精确标注response.dataPOST 场景可同时约束请求体与响应axios.create返回可调用、可链式创建的AxiosInstancebaseURL/timeout/headers 从创建起即受类型保护请求拦截器必须标注InternalAxiosRequestConfigheaders必填且为AxiosHeaders实例响应拦截器标注AxiosResponseaxios.isAxiosErrorT()与axios.isCancelT()是catch块中收窄错误类型的标准手段配合AxiosError的静态错误码可做精确分支处理tsconfig 首选moduleResolution: node16无法使用时编译 CJS 需开启esModuleInterop。主要参考文件index.d.ts、index.d.cts、package.json、lib/axios.js、lib/core/InterceptorManager.js、TypeScript 文档页、tests/module/cjs 与 tests/module/esm 类型测试。【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考