首页
/ axios TypeScript 完全实战指南:类型导入、泛型请求、有类型实例、拦截器与错误守卫

axios TypeScript 完全实战指南:类型导入、泛型请求、有类型实例、拦截器与错误守卫

2026-09-06 12:05:37作者:侯霆垣

axios 在 v1.x 中随 npm 包内置完整的 TypeScript 类型定义,开发者无需额外安装 @types 依赖,即可获得从请求、响应、错误到实例与拦截器全链路的类型安全。本篇以官方 Getting Started 中的 TypeScript 示例文档 为主线,完整覆盖类型导入、泛型请求、函数封装、POST 类型标注、有类型实例、有类型拦截器、错误类型收窄等实战写法,并结合 index.d.ts 源码级定义与模块类型测试,说明每个类型背后的实际契约与模块配置注意事项,帮助你在浏览器与 Node.js 项目中写出零类型报错的 axios 客户端代码。

axios 随包提供双格式类型定义

axios 采用 ESM/CJS 双发布策略,类型文件也随之提供两份入口。在 package.jsonexports 字段中可以看到明确的映射关系:

{
  "types": {
    "require": "./index.d.cts",
    "default": "./index.d.ts"
  }
}

也就是说,当 TypeScript 以 ESM 方式解析时加载 index.d.ts,以 CommonJS require 方式解析时加载 index.d.cts。这正是后文模块配置注意事项的根源。

类型文件的顶层导出结构可以在 index.d.ts 中确认:默认导出 axios 的类型是 AxiosStatic,它继承自 AxiosInstance,并挂载了 Axios 类、AxiosErrorisAxiosErrorisCanceltoFormDatamergeConfig 等全部静态成员:

export interface AxiosStatic extends AxiosInstance {
  Cancel: typeof CanceledError;
  CancelToken: CancelTokenStatic;
  Axios: typeof Axios;
  AxiosError: typeof AxiosError;
  HttpStatusCode: typeof HttpStatusCode;
  readonly VERSION: string;
  isCancel: typeof isCancel;
  // ...all, spread, isAxiosError, toFormData, formToJSON, getAdapter, mergeConfig
}

declare const axios: AxiosStatic;
export default axios;

这意味着 import axios from "axios" 得到的默认导出天然带有完整的请求方法与静态工具函数签名。

导入类型

axios 的公共类型均从 "axios" 直接导出,无需深入内部路径:

import axios from "axios";
import type { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios";

三个最常用的类型在 index.d.ts 中的定义如下,理解它们的泛型参数是后面所有写法的基础:

  • AxiosRequestConfig<D = any, P = any>L391):D 表示请求体数据类型,P 表示 query 参数类型,包含 urlbaseURLtimeoutheadersparamsvalidateStatussignal 等全部请求配置字段;
  • AxiosResponse<T = any, D = any, H = {}, P = any>L515):data: TstatusstatusTextheaders,并且 config 字段类型是 InternalAxiosRequestConfig<D, P>
  • AxiosError<T = unknown, D = any, P = any> extends ErrorL524):带有 configcoderesponse?: AxiosResponse<T, D, {}, P>isAxiosError: boolean 属性,并声明了 ERR_NETWORKETIMEDOUTECONNABORTED 等静态错误码常量。

另外,AxiosRequestConfigL483 有一个别名 RawAxiosRequestConfig,二者等价,可互换使用。

为 GET 请求标注响应类型

通过响应泛型参数告诉 TypeScript 数据的具体形状。axios.get<T> 的返回是 Promise<AxiosResponseResult<T, R, D, P>>,当未提供自定义响应类型 R 时,结果就是标准的 AxiosResponse<T>(见 index.d.tsAxiosResponseResult 定义),因此 response.data 会被精确标注为你传入的类型:

import axios from "axios";

type Post = {
  userId: number;
  id: number;
  title: string;
  body: string;
};

const response = await axios.get<Post>("https://jsonplaceholder.typicode.com/posts/1");

console.log(response.data.title); // TypeScript knows this is a string

由于 Post.title 被声明为 string,如果误写 response.data.title.length 之外的属性(例如 response.data.content)会立即在编译期报错,而不是拖到运行时。

将请求封装为有类型的函数

把请求包裹在显式返回类型的函数里,可以在调用链的更上游就获得类型保障:

import axios, { AxiosResponse } from "axios";

type Post = {
  userId: number;
  id: number;
  title: string;
  body: string;
};

const getPost = async (id: number): Promise<Post> => {
  const response = await axios.get<Post>(
    `https://jsonplaceholder.typicode.com/posts/${id}`
  );
  return response.data;
};

这里 await axios.get<Post>(...) 得到 AxiosResponse<Post>,解构出的 response.data 正是 Post,与函数声明的 Promise<Post> 返回值一致。把 data 而非整个 AxiosResponse 暴露给业务层,可以让上层代码只关心数据形状。

为 POST 请求同时标注请求体与响应

请求方法除响应类型 T 外,还支持第三个泛型位置描述请求体类型 D。实践中最常见的是先定义请求体与响应两个类型,再用泛型约束响应:

type CreatePostBody = {
  title: string;
  body: string;
  userId: number;
};

type CreatePostResponse = CreatePostBody & { id: number };

const createPost = async (data: CreatePostBody): Promise<CreatePostResponse> => {
  const response = await axios.post<CreatePostResponse>(
    "https://jsonplaceholder.typicode.com/posts",
    data
  );
  return response.data;
};

如果希望显式利用 D 位置(请求体类型),可以把请求方法签名的 <T, R, D, P> 四个泛型都用上——axios.post<CreatePostResponse, undefined, CreatePostBody>(...),此时 response.config.data 也会被推断为 CreatePostBody | undefined。完整的请求数据与 query 参数双泛型用法(AxiosRequestConfig<D, P>paramsSerializer 的类型推断)可参阅进阶文档 docs/pages/advanced/type-script.md

创建有类型的 axios 实例

AxiosInstance 标注 axios.create 的返回值,把 baseURLtimeout、默认请求头等“烘焙”进客户端,后续所有请求方法都继承该类型:

import axios from "axios";
import type { AxiosInstance } from "axios";

const api: AxiosInstance = axios.create({
  baseURL: "https://api.example.com",
  timeout: 5000,
});

从源码看(index.d.ts),AxiosInstance 继承自 Axios 接口,并额外提供:

  • 两个可调用签名:api(config)api(url, config),分别对应以完整配置或 URL+配置发起请求;
  • create(config?: CreateAxiosDefaults): AxiosInstance,支持从已有实例再派生子实例;
  • defaults 属性,类型是 Omit<AxiosDefaults, 'headers'> 加上按方法分组的 headers

CreateAxiosDefaultsL508-L513)与 AxiosRequestConfig 的区别在于 headers 的写法更宽松,允许传入 RawAxiosRequestHeaders | AxiosHeaders | Partial<HeadersDefaults>,因此实例级默认头可以写成 { common: {...}, post: {...} } 的形式。

请求拦截器:必须使用 InternalAxiosRequestConfig

v1.x 中请求拦截器必须使用 InternalAxiosRequestConfig(而不是 AxiosRequestConfig)。原因在于二者的 headers 字段类型不同(index.d.ts):

export interface InternalAxiosRequestConfig<D = any, P = any> extends AxiosRequestConfig<D, P> {
  headers: AxiosRequestHeaders;  // 必填,且是 AxiosHeaders 实例
}

AxiosRequestConfigheaders 是可选的,且允许普通对象或 AxiosHeaders;而请求经过默认合并后,headers 一定已经是一个 AxiosHeaders 实例(提供 .set().get() 等方法)。拦截器拿到的是合并后的内部配置,所以类型必须与运行时形态一致:

import 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)
);

注意 config.headers.set(...) 这种调用方式正是 headers 被标注为 AxiosRequestHeadersAxiosHeaders 实例类型)的直接体现;如果误用 AxiosRequestConfig,在开启严格检查时 set 方法不一定可用。

错误类型收窄:isAxiosError 类型守卫

catch 块中 error 默认是 unknown。使用 axios.isAxiosError<T>() 可以安全地把错误收窄为 AxiosError<T>,从而在编译期安全访问 error.response?.dataerror.response?.status

import axios, { AxiosError } from "axios";

type ApiError = {
  message: string;
  code: number;
};

try {
  await axios.get("/api/protected-resource");
} catch (error) {
  if (axios.isAxiosError<ApiError>(error)) {
    // error.response?.data is typed as ApiError
    console.error(error.response?.data.message);
    console.error(error.response?.status);
  } else {
    throw error;
  }
}

类型层面,该守卫在 index.d.ts 中声明为类型谓词:

export function isAxiosError<T = any, D = any, P = any>(
  payload: any
): payload is AxiosError<T, D, P>;

运行时实现极为简洁,见 lib/helpers/isAxiosError.js

export default function isAxiosError(payload) {
  return utils.isObject(payload) && payload.isAxiosError === true;
}

即只要对象上带有 isAxiosError === true 标记(该属性在 AxiosError 构造时写入)就判定为 axios 错误——这也解释了为什么跨 bundle 边界(如错误从 Worker 抛出)后类型收窄依然有效。类似的守卫还有 axios.isCancel<T>()L755),用于把取消错误收窄为 CanceledError<T>;而 AxiosError 上声明的 ERR_NETWORKETIMEDOUTECONNABORTED 等静态错误码常量(L550-L563)则可用于按 error.code 做分支处理。

TypeScript 配置注意事项

由于 axios 同时发布 ESM(module 字段指向 ./index.js,默认导出)和 CJS(main 指向 ./dist/node/axios.cjsmodule.exports),不同 tsconfig 下需要满足不同的前提:

场景 配置要求
推荐 "moduleResolution": "node16"(由 "module": "node16" 隐含),需要 TypeScript 4.7+
编译到 CJS 且无法使用 node16 必须开启 "esModuleInterop": true,否则默认导出互操作会报错
用 TypeScript 检查 CJS 的 JavaScript 代码 唯一选择是 "moduleResolution": "node16"

仓库自身的模块类型测试印证了这些约束:

也就是说,如果你的老项目还在 "moduleResolution": "node"(Node10 解析)下以 CJS 方式 require axios,补上 "esModuleInterop": true 即可正常获得类型支持;而新版 TypeScript 项目直接采用 node16 解析是最省事、也最能正确匹配上面 exports 映射(含 .d.cts.d.ts 分流)的方案。

常用类型速查

类型 定义位置 用途
AxiosRequestConfig<D, P> index.d.ts#L391 描述请求配置,D 为请求体类型、P 为 query 参数类型
RawAxiosRequestConfig index.d.ts#L483 AxiosRequestConfig 的别名
InternalAxiosRequestConfig<D, P> index.d.ts#L485 请求拦截器专用,headers 必为 AxiosRequestHeaders
AxiosResponse<T, D, H, P> index.d.ts#L515 响应对象,data: Tconfig: InternalAxiosRequestConfig<D, P>
AxiosError<T, D, P> index.d.ts#L524 错误类,含 response?code? 与静态错误码
AxiosPromise<T, D, P> index.d.ts#L580 Promise<AxiosResponse<T, D, {}, P>> 的别名
AxiosInstance index.d.ts#L710 可调用实例,axios.create 的返回类型
AxiosStatic index.d.ts#L766 默认导出 axios 的类型
isAxiosError<T, D, P> index.d.ts#L749 错误收窄的类型守卫
isCancel<T, D, P> index.d.ts#L755 取消错误收窄的类型守卫

掌握以上类型后,从单条 axios.get<Post>(...) 到带 AxiosInstance、拦截器与错误守卫的完整客户端,都可以在编译期完成验证。若需要更深入的用法——如请求数据与 query 参数的双泛型推断、paramsSerializer 的类型、Symbol 键自定义配置的模块增强(declare module "axios")——可继续参阅 TypeScript 进阶文档 以及 CommonJS 场景的 CommonJS 示例文档

登录后查看全文
热门项目推荐
相关项目推荐