axios 1.x TypeScript 实战指南:类型导入、请求泛型、拦截器配置与错误类型收窄

发布时间:2026/9/7 2:19:24
axios 1.x TypeScript 实战指南:类型导入、请求泛型、拦截器配置与错误类型收窄 axios 1.x TypeScript 实战指南类型导入、请求泛型、拦截器配置与错误类型收窄【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios本文以 axios 仓库中官方的 TypeScript 入门示例文档docs/fr/pages/getting-started/examples/typescript.md英文原版见 docs/pages/getting-started/examples/typescript.md为主体系统讲解在 axios 1.x 中如何导入类型、用泛型约束请求与响应、创建带类型实例、编写带类型的拦截器并收窄错误类型。读完本篇你将掌握 axios 与 TypeScript 集成的完整用法并能对照 index.d.ts 源码理解每个 API 背后的类型签名设计。类型从哪里来随包发布的类型定义axios 开箱即用地附带 TypeScript 类型定义无需额外安装types/axios。类型入口就是仓库根目录下的 index.d.tsESM 类型787 行与 index.d.ctsCJS 类型二者由package.json的exports映射在模块解析时自动选择types: index.d.ts, exports: { .: { types: { require: ./index.d.cts, default: ./index.d.ts }, default: { require: ./dist/node/axios.cjs, default: ./index.js } } }也就是说axios 同时双份发布 ESM 与 CJS 产物ESM 入口是 index.jsCJS 入口是dist/node/axios.cjs类型文件也随之分成了index.d.ts和index.d.cts两份。这也解释了后文「TypeScript 配置注意事项」一节中为什么不同moduleResolution配置下表现会有差异。类型定义的最低 TypeScript 版本要求直接写在 index.d.ts#L1 的第一行// TypeScript Version: 4.7因此 axios 的类型系统要求TypeScript 4.7 或更高版本这与后文推荐moduleResolution: node16由 TS 4.7 引入的结论是一致的。导入类型axios 的类型可以直接从axios模块命名导入。仓库根目录的 index.js 会把默认导出的 axios 实例「解包」为与静态属性一致的命名导出create、Axios、AxiosError、isAxiosError、AxiosHeaders等因此在 TypeScript 侧同样可以按命名方式引用类型import axios from axios; import type { AxiosRequestConfig, AxiosResponse, AxiosError } from axios;建议在只需要类型信息时使用import type这样编译器会在编译期将其完全擦除不产生任何运行时开销。这几个基础类型在 index.d.ts 中的定义位置分别是AxiosRequestConfigindex.d.ts#L391-L480、AxiosResponseindex.d.ts#L515-L522、AxiosErrorindex.d.ts#L524-L564。给请求打上类型响应泛型使用响应类型的泛型参数即可告知 TypeScript 你的数据将呈现什么形状。以下示例沿用官方文档以 jsonplaceholder 的 posts 接口为例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泛型参数的底层签名从 index.d.ts#L654-L708 可以看到Axios类上每个请求方法都接受四个泛型参数getT any, R AxiosResponseDefault, D any, P any( url: string, config?: AxiosRequestConfigD, P ): PromiseAxiosResponseResultT, R, D, P;T响应data的类型即上面例子中传入的PostR响应对象本身的结果形状默认为AxiosResponse即Promiseresolve 出response.data、response.status等标准结构D请求体的类型最终体现在config.data?: D上见 index.d.ts#L403P查询参数的类型体现在config.params?: P上见 index.d.ts#L399。AxiosResponse接口本身也带有泛型index.d.ts#L515-L522export interface AxiosResponseT any, D any, H {}, P any { data: T; status: number; statusText: string; headers: (H RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfigD, P; request?: any; }因此axios.getPost(...)返回的实际上是PromiseAxiosResponsePost, any, {}, anyresponse.data被精确推断为Post.title自然就是string。给函数打上类型将请求封装进带显式返回类型的函数可以获得最大的类型安全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; };在 lib/core/Axios.js#L268-L306 中可以看到get、post等便捷方法在运行时都是对核心request()方法的别名封装无参方法如delete/get/head/options走一个分支带数据的方法post/put/patch/query走另一个分支并额外生成postForm等 Form 变体。类型定义则为这些别名逐一声明了与request相同的四泛型签名所以无论用axios.getPost(url)还是axios.requestPost({ url })类型行为完全一致。给 POST 请求打类型POST 场景可以同时约束请求体和期望的响应。注意post的泛型参数顺序是postT, R, D, P——第一个泛型仍是响应T请求体类型是第三个Dtype 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; };对照签名index.d.ts#L673-L677postT any, R AxiosResponseDefault, D any, P any( url: string, data?: D, config?: AxiosRequestConfigD, P ): PromiseAxiosResponseResultT, R, D, P;如果还想连请求体一起约束可以写成axios.postCreatePostResponse, AxiosResponseCreatePostResponse, CreatePostBody(url, data)此时data参数会被推断为CreatePostBody传错字段会直接编译报错。带类型的 axios 实例创建类型化实例把 base URL 与默认头固定在其中import axios from axios; import type { AxiosInstance } from axios; const api: AxiosInstance axios.create({ baseURL: https://api.example.com, timeout: 5000, });axios.create的返回类型在 index.d.ts#L719 中声明为AxiosInstance其参数类型是CreateAxiosDefaultsindex.d.ts#L508-L513即OmitAxiosRequestConfig, headers加上一个更宽松的头类型RawAxiosRequestHeaders | AxiosHeaders | PartialHeadersDefaults——因为create时传入的只是「默认值原料」真正的AxiosHeaders实例会在实例化之后由内部构造。从 lib/axios.js#L28-L47 的createInstance实现看axios.create(config)会new Axios(defaultConfig)、把原型方法与实例状态合并绑定并挂上instance.create工厂方法内部通过mergeConfig(defaultConfig, instanceConfig)继承父实例配置。默认导出的axios本身就是用defaults创建的这样一个实例。AxiosInstance还定义了两个可调用重载index.d.ts#L710-L717所以除了api.get(...)之外还可以像 fetch 一样直接以配置对象或 URL 字符串调用api({ url: /posts, method: get }); api(/posts, { params: { id: 1 } });带类型的拦截器在 v1.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) );示例中api即上一小节创建的实例。为什么必须是 InternalAxiosRequestConfig两者的定义差异只有两行index.d.ts#L485-L487export interface InternalAxiosRequestConfigD any, P any extends AxiosRequestConfigD, P { headers: AxiosRequestHeaders; }AxiosRequestConfig.headers是可选的、类型较宽的联合类型RawAxiosRequestHeaders MethodsHeaders或AxiosHeaders而InternalAxiosRequestConfig.headers是必填的AxiosRequestHeadersRawAxiosRequestHeaders AxiosHeadersindex.d.ts#L130。这对应了运行时事实请求进入拦截器链之前config.headers一定已被构造为AxiosHeaders实例——见 lib/core/Axios.js#L153-L164在_request中 headers 会先按方法合并再执行config.headers AxiosHeaders.concat(contextHeaders, headers)。正因如此示例里的config.headers.set(Authorization, ...)才能通过类型检查set方法是AxiosHeaders类成员index.d.ts#L30-L36而不是普通对象字面量上的属性。拦截器的管理器接口为AxiosInterceptorManagerVindex.d.ts#L639-L644请求拦截器use的第三个可选参数类型是AxiosInterceptorOptionsexport interface AxiosInterceptorOptions { synchronous?: boolean; runWhen?: ((config: InternalAxiosRequestConfig) boolean) | null; }运行时实现见 lib/core/InterceptorManager.js#L67-L92use(fulfilled, rejected, options)返回一个数字id可用eject(id)移除、clear()清空。所以给拦截器注册留一个变量是常见写法const id api.interceptors.request.use( (config) { config.headers.set(Authorization, Bearer ${getToken()}); return config; }, (error) Promise.reject(error) ); // 不再需要时api.interceptors.request.eject(id);此外注意AxiosRequestInterceptorUse的签名是(value: T) T | PromiseTindex.d.ts#L618-L625请求拦截器既允许同步返回config也允许返回PromiseInternalAxiosRequestConfig。给错误打上类型捕获错误时使用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 被推断为 ApiError console.error(error.response?.data.message); console.error(error.response?.status); } else { throw error; } }isAxiosError在类型层面是一个类型守卫index.d.ts#L749-L751export function isAxiosErrorT any, D any, P any( payload: any ): payload is AxiosErrorT, D, P;泛型T透传给AxiosErrorT而AxiosError.response?: AxiosResponseT, D, {}, Pindex.d.ts#L536所以传入ApiError后error.response?.data就被精确收窄为ApiError.message可直接访问。运行时判断逻辑非常轻量见 lib/helpers/isAxiosError.js#L12-L14export default function isAxiosError(payload) { return utils.isObject(payload) payload.isAxiosError true; }它只检查对象上是否带有isAxiosError true标记AxiosError构造时写入。AxiosError还暴露了一组静态错误码常量index.d.ts#L550-L563如ERR_NETWORK、ERR_CANCELED、ETIMEDOUT配合error.code字段可用于在catch分支中区分网络故障、取消与超时。TypeScript 配置注意事项由于 axios 同时发布 ESM 与 CJS 两个版本tsconfig.json中有一些需要注意的细节推荐配置是moduleResolution: node16由module: node16隐含需要 TypeScript 4.7 或更高版本。只有该解析模式才会读取package.json中的exports映射从而按require/import两种场景正确选中index.d.cts或index.d.ts。axios 仓库自己的 tsconfig.json 也正是使用module: node16strict: true如果你把 TypeScript 编译为 CJS、又无法使用moduleResolution: node16请开启esModuleInterop: true以兼容 axios 默认导出与 CJS 互操作如果用 TypeScript 来给 CJS 的 JavaScript 代码做类型检查唯一可行的选项是moduleResolution: node16。延伸阅读类型定义全文index.d.tsESM、index.d.ctsCJS核心类与默认实例lib/core/Axios.js、lib/axios.js拦截器管理器实现lib/core/InterceptorManager.js更深入的 TypeScript 主题module augmentation 扩展AxiosRequestConfig自定义符号属性、D/P泛型在response.config上的保留等可继续阅读 docs/pages/advanced/type-script.md类型层面的回归测试位于 tests/module/cjs/tests/cjs-typing.ts 与 tests/module/esm/tests/typings.module.test.js可作为各模块系统下类型行为的验证参考【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考