项目:my-ts-axios | 版本:v1.0 | 生成日期:2026-10-06 | 依据:源码分析
库的唯一入口是 src/index.ts,仅两行有效代码,构成三层导出面:
import axios from './axios'
export * from './types' // 全部公共类型(AxiosRequestConfig、AxiosResponse、AxiosError、CancelToken……)
export default axios // 默认导出:混合对象 axios 实例(AxiosStatic)类型导出清单(src/types/index.ts):Method、AxiosRequestConfig、AxiosProgressEvent、ParamsSerializer、ParamsSerializerOptions、FormSerializerOptions、TransitionalOptions、AxiosProxyConfig、AxiosResponse、AxiosPromise、AxiosAdapter、AxiosError(interface 形态)、Axios、AxiosInstance、AxiosClassStatic、AxiosStatic、AxiosInterceptorManager、ResolvedFn、RejectedFn、AxiosTransformer、CancelToken、Canceler、CancelExecutor、CancelTokenSource、CancelTokenStatic、Cancel、CancelStatic、AxiosBasicCredentials。
这个极简入口背后有三条导出设计原则。原则一:值出口只有一个——所有运行时值都收敛在 default axios(及其挂载的静态成员)上,index.ts 不额外散落命名导出,这使消费方"一个 import 拿到全部";类型则相反,全部走 export * 命名导出,便于按需引用而不引入运行时代价。原则二:内部构件可逃逸——Axios 类、mergeConfig、getAdapter、AxiosHeaders、toFormData 这些"库内部件"被有意挂到静态面(axios.ts 第 43~53 行),调用方可以不经过 HTTP 直接复用(例如单独用 mergeConfig 合并自己的配置对象),同时 smoke-dist.cjs 逐一断言这些出口,防止发布时意外缺失。原则三:无副作用的类型面——export * from './types' 只含类型(import type 引用实现类的地方都用了 type-only import),保证类型导出在编译后完全消失,不增大产物体积。
src/axios.ts 在默认实例上挂载的全部成员(与 smoke-dist.cjs 的导出断言一一对应):
| 成员 | 类型/签名 | 说明 |
|---|---|---|
axios(config) / axios(url, config?) | (config) => AxiosPromise | 绑定了 Axios.prototype.request 的可调用实例 |
axios.request(config) | 同上 | 同上 |
axios.get/delete/head/options(url, config?) | (url, config?) => AxiosPromise | 无请求体方法族 |
axios.post/put/patch(url, data?, config?) | (url, data?, config?) => AxiosPromise | 带请求体方法族 |
axios.postForm/putForm/patchForm(url, data?, config?) | 同上 | 自动 toFormData 并置 Content-Type: multipart/form-data(src/core/Axios.ts _requestForm) |
axios.getUri(config?) | (config?) => string | 合并 defaults 后 transformURL,不发起请求 |
axios.create(config?) | (config?) => AxiosInstance | createInstance(mergeConfig(defaults, config)),总是以全局 defaults 为底 |
axios.defaults | AxiosRequestConfig | 实例默认配置(可运行时修改) |
axios.interceptors | { request, response } | 两个 InterceptorManager |
axios.CancelToken | CancelTokenStatic | new CancelToken(executor) 与 CancelToken.source() |
axios.Cancel | CancelStatic | 旧版取消原因类 |
axios.isCancel(value) | (value: any) => boolean | 识别 Cancel 或 CanceledError |
axios.all(promises) | <T>(...) => Promise<T[]> | 即 Promise.all |
axios.spread(callback) | <T, R>(cb) => (arr: T[]) => R | 展开数组为回调参数 |
axios.Axios | AxiosClassStatic | 类本身,允许用户自行 new Axios(config) |
axios.AxiosHeaders | typeof AxiosHeaders | 首部类 |
axios.AxiosError / axios.CanceledError | 错误类 | 供调用方 instanceof 判定 |
axios.VERSION | string | 常量 '0.2.1'(与 package.json 同步由 scripts/smoke-dist.cjs 在发布前校验) |
axios.HttpStatusCode | 状态码常量表 | HttpStatusCode.NotFound === 404 等 |
axios.mergeConfig / axios.getAdapter | 内部工具静态化 | 与官方 v1 对齐的暴露 |
axios.isAxiosError(payload) | (payload: any) => boolean | payload instanceof AxiosError |
axios.toFormData / axios.formToJSON | 表单工具 | 见第 02 篇第 8 节 |
实例创建后的可用面(createInstance 拷贝而来):request、get、delete、head、options、post、put、patch、postForm、putForm、patchForm、getUri、defaults、interceptors。create 与上表中的静态 API 不会被拷贝到 axios.create() 产出的实例上(它们只存在于全局 axios 函数对象上)。
静态面与实例面的分工可以概括为:实例面只谈"这一次请求"(方法族、defaults、interceptors 都是实例私有的,axios.create 的意义就是隔离配置与拦截器),静态面提供"与具体请求无关的公共设施"(取消原语、并发工具、错误类、表单工具、合并/适配器选择器)。使用上有两个常见误区可借此澄清:一是以为 instance.interceptors 会影响全局——不会,每个 create 产物都有独立的两个 InterceptorManager;二是试图在实例上找 CancelToken 等——应从全局 axios 或独立 import 获取。此外 axios.all/axios.spread 是历史遗留的并发糖(Promise.all + 参数展开),新代码直接用 Promise.all 即可,保留它们只为对齐老版 axios 的使用习惯。
关于方法签名还有一组横切约定值得单独说明。泛型的位置:从类型面看,get<T = any>(url, config?): AxiosPromise<T> 的泛型参数 T 落在响应 data 字段上(AxiosPromise<T> = Promise<AxiosResponse<T>>),但注意实现侧 src/core/Axios.ts 的方法返回类型标注为非泛型的 AxiosPromise——泛型仅存在于 types/index.ts 的 Axios/AxiosInstance 接口上,运行时无从约束,response.data 的实际类型由调用方的泛型实参决定,这是一切 HTTP 库的通用取舍。参数顺序:带体方法是 (url, data?, config?)、无体方法是 (url, config?),与官方 axios 完全一致,迁移成本为零;request 则额外兼容 (url, config) 形式。同步与异步的边界:除 getUri(同步返回字符串)外所有方法都返回 Promise,即便请求在链上前段就已被取消(预检失败),调用方拿到的也仍是一个 rejected Promise 而非同步异常——唯一的例外是 synchronous 快路径下请求拦截器自身同步抛错的场景(见第 05 篇流程一的边界说明)。
类型明细按"请求—响应—错误—辅助"四组展开。阅读时建议始终对照第 04 篇的形态变化:类型声明的是"契约形态",而同一对象在链路不同阶段会呈现不同"实例形态",二者不可混为一谈。
声明于 src/types/index.ts 第 24~86 行,末尾带 [propName: string]: any 索引签名(宽松扩展点)。字段、类型、生效默认值与说明:
| 字段 | 类型 | 默认值(src/defaults.ts) | 说明 |
|---|---|---|---|
url | string | 无(不继承) | 请求路径,fromVal2Strat 不从 defaults 继承 |
method | Method(8 个大写/小写字面量联合) | 'get' | request 入口统一转小写 |
data | any | 无(不继承) | 请求体 |
params | any | 无(不继承) | URL 查询参数对象 |
headers | any(推荐 {common,method,顶层} 三层) | headers.common.Accept 等 | 合并策略为深合并,flattenHeaders 拍平 |
responseType | XMLHttpRequestResponseType | 'stream' | 无 | 'stream' 仅 http 适配器生效;xhr 显式跳过 |
timeout | number | 0(不限时) | 毫秒;三适配器均支持 |
transformRequest | AxiosTransformer | AxiosTransformer[] | 默认转换器数组 | 请求体管道 |
transformResponse | 同上 | 默认转换器数组 | 响应体管道,受 transitional.forcedJSONParsing 控制 |
cancelToken | CancelToken | 无 | 旧版取消令牌 |
signal | AbortSignal | 无 | 新版取消信号 |
withCredentials | boolean | 无 | xhr / fetch 生效 |
xsrfCookieName / xsrfHeaderName | string | 'XSRF-TOKEN' / 'X-XSRF-TOKEN' | XSRF 防护 |
withXSRFToken | boolean | ((config) => boolean) | 无 | 函数返回真值或非 false 才考虑附加 XSRF 头 |
onDownloadProgress / onUploadProgress | (e: AxiosProgressEvent) => void | 无 | 进度回调;fetch 无上传进度 |
auth | AxiosBasicCredentials | 无 | Basic 认证,deepMerge 策略 |
validateStatus | (status) => boolean | 200 <= status < 300 | settle 判定 |
paramsSerializer | ParamsSerializer | ParamsSerializerOptions | 无 | 函数式或 {serialize, encode, indexes} |
baseURL | string | 无 | 非 undefined 且 allowAbsoluteUrls !== false 且 url 非绝对地址时拼接 |
allowAbsoluteUrls | boolean | 无(按 undefined 视同 true) | 控制绝对 URL 是否仍拼 baseURL |
adapter | AxiosAdapter | AxiosAdapter[] | string | string[] | 无(内部顺序 ['xhr','http','fetch']) | 命名/自定义/回退数组 |
formSerializer | FormSerializerOptions | 无 | dots/metaTokens/indexes/visitor/maxDepth(visitor/indexes/dots 已消费,metaTokens/maxDepth 在 src/helpers/formData.ts 中声明未使用 ⚠️ 待确认) |
transitional | TransitionalOptions | {silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false} | silentJSONParsing 声明但源码未消费 ⚠️ 待确认 |
env | {FormData?: any} | typeof FormData !== 'undefined' ? FormData : undefined | 非浏览器环境注入 FormData 实现 |
fetchOptions | Record<string, any> | 无 | fetch 适配器 init 透传(不可覆盖核心字段) |
| Node 专属 | httpAgent/httpsAgent/proxy(对象或 false)/maxRedirects/maxContentLength/maxBodyLength/decompress/responseEncoding/socketPath/transport/maxRate/beforeRedirect/family/lookup/insecureHTTPParser | maxRedirects 缺省 5;decompress 缺省 true | 仅 http 适配器消费 |
这三个类型共同构成"一次调用的应答契约"。AxiosResponse 是唯一以值形态出现在成功路径上的结构;AxiosPromise 的存在让 Axios 接口的方法签名可以省略 Promise< 包装,且允许下游以 interface MyResponse extends AxiosPromise<User> 的方式定制;AxiosAdapter 则是适配器生态的准入证——任何实现该签名的函数都可以通过 config.adapter 注入,包括测试用的 mock 适配器。
| 类型 | 形状 | 说明 |
|---|---|---|
AxiosResponse<T = any> | {data: T; status: number; statusText: string; headers: any; config: AxiosRequestConfig; request: any} | headers 为小写键普通对象(三适配器一致);request 在 fetch 适配器中恒为 null,xhr 为 XMLHttpRequest 实例,http 为 ClientRequest |
AxiosPromise<T = any> | interface extends Promise<AxiosResponse<T>> | 保留 interface 形式以便 extends(源码注释明确) |
AxiosAdapter | (config: AxiosRequestConfig) => AxiosPromise | 适配器契约签名 |
错误类型刻意采用"interface 描述 + class 实现"的双轨制:调用方在 catch 里可以引用 AxiosError interface 获得完整字段提示,而运行时判定应使用 isAxiosError()(instanceof)而非检查 isAxiosError 布尔属性——前者不可伪造,后者任何对象都能模拟。
运行时实现在 src/helpers/error.ts(class AxiosError extends Error),类型面在 types/index.ts 提供 interface:
| 字段 | 类型 | 说明 |
|---|---|---|
message / name | string | name 恒为 'AxiosError'(CanceledError 子类改写为 'CanceledError') |
config | AxiosRequestConfig | 发起请求的配置引用 |
code | string | null | 取值见 AxiosErrorCodes(13 个:ERR_BAD_OPTION_VALUE、ERR_BAD_OPTION、ECONNABORTED、ETIMEDOUT、ERR_NETWORK、ERR_FR_TOO_MANY_REDIRECTS、ERR_DEPRECATED、ERR_BAD_RESPONSE、ERR_BAD_REQUEST、ERR_CANCELED、ERR_NOT_SUPPORT、ERR_INVALID_URL、ERR_PROXY;其中 ERR_BAD_OPTION_VALUE/ERR_BAD_OPTION/ERR_DEPRECATED 声明未使用,属对齐官方码表) |
status | number | null | HTTP 状态码;由 settle → createError 第 6 参写入 |
request | any | 底层请求对象(可为 undefined) |
response | AxiosResponse(可选) | HTTP 错误时携带,且 data 已过 transformResponse |
isAxiosError | boolean | 恒 true;另提供 isAxiosError() 用 instanceof 判定 |
toJSON() | 方法 | 序列化 message/name/code/status/config/response |
取消系列类型的分工体现了双轨设计在类型层的投影:CancelToken/Canceler/CancelExecutor/CancelTokenSource/CancelTokenStatic 五个类型完整刻画了旧轨道的"令牌—触发器—工厂"三元结构,使用方通常只接触 CancelToken.source() 与 Canceler 两个入口;Cancel 与 CancelStatic 描述旧轨道的取消原因值;新轨道不引入任何自定义类型——直接复用平台自带的 AbortSignal(AxiosRequestConfig.signal 字段)。两轨在类型上唯一的交点是 isCancel: (value: any) => boolean,它同时承认两轨的取消值。拦截器系列(AxiosInterceptorManager/ResolvedFn/RejectedFn)则保持与官方一致的最小面,synchronous/runWhen 选项的接口(InterceptorOptions)定义在 src/core/InterceptorManager.ts 内部而未进入公共类型面,属于"实现先行、契约后补"的过渡状态。
| 类型 | 形状 |
|---|---|
CancelToken | {promise: Promise<Cancel>; reason?: Cancel; throwIfRequested(): void} |
Canceler | (message?: string) => void |
CancelExecutor | (cancel: Canceler) => void |
CancelTokenSource | {token: CancelToken; cancel: Canceler} |
CancelTokenStatic | new (executor) => CancelToken + static source() |
Cancel | {message?: string} |
AxiosInterceptorManager<T> | use(resolved, rejected?) => number;eject(id) => void(源码另有 runWhen/synchronous 选项实现在 InterceptorOptions,types 中未导出该选项接口 ⚠️ 待确认:InterceptorOptions 定义于 src/core/InterceptorManager.ts 且未从 src/types 导出) |
AxiosTransformer | (data: any, headers?: any) => any |
AxiosProgressEvent | {loaded; total?; progress?; bytes?; rate?; estimated?; upload?; download?; lengthComputable?; event?} |
AxiosProxyConfig | {host; port; auth?: {username; password}; protocol?} |
AxiosBasicCredentials | {username: string; password: string} |
对照口径说明:官方行为以 axios v1.x 的公开文档与其 GitHub 仓库实现为准;本清单只列可从源码直接验证的差异点,凡属"官方也如此"的行为(如 validateStatus 默认区间、axios.all/spread 的实现、baseURL 拼接规则)不在此列。逐条核对时建议同时打开第 03 篇引用的源文件。
以下差异均以本项目源码与官方公开行为对照得出,供使用与扩展时参考:
Cancel 与 CanceledError 是两个独立类。官方 v1 中 axios.Cancel 即 CanceledError 的别名;本项目 src/cancel/Cancel.ts 是仅含 message 的简单类,CanceledError 继承 AxiosError。isCancel 对两者都返回 true,因此判定语义兼容。AxiosHeaders 是可用但非强制的容器(flattenHeaders 接受实例并 toJSON)。官方 v1 的响应 headers 是 AxiosHeaders 实例;本项目三适配器均返回小写键普通对象。src/core/adapters/http.ts _send 递归),未使用 follow-redirects:方法改写规则为"301/302 且方法为 post、或 303 且方法非 get/head 时改写为 GET 并丢体(同时删除 content-* 头)",maxRedirects 默认 5,跨 host 重定向剥离 Authorization/Proxy-Authorization/Cookie,beforeRedirect(options, responseDetails) 抛错即中断。onUploadProgress 在 fetch.ts 中未实现(fetch API 限制),下载进度通过 body.getReader() 循环读取实现。transitional.silentJSONParsing、FormSerializerOptions.metaTokens/maxDepth 声明但未消费 ⚠️ 待确认是否为后续计划项。AxiosHeaders 为官方方法子集:无 forEach、match、asType、setCookie、getSetCookie、accessor 等方法;has 语义与官方一致(存在即真)。synchronous === true 才走同步路径(Axios.ts 第 71 行 every),与官方一致;但响应拦截器无 synchronous 概念(官方同样只对请求拦截器生效)。axios.create 总是以全局 defaults 为合并底(axios.ts 第 28~30 行),且创建出的实例没有 .create 与静态 API——与官方行为一致,此处列出仅为提醒。ERR_PROXY 为本项目自增(官方 proxy 错误通常以 ERR_BAD_REQUEST/普通 Error 呈现),用于 CONNECT 隧道失败与代理请求失败两类场景(http.ts 第 486、524 行)。responseType: 'stream' 仅 Node 有效:xhr 适配器显式跳过该值(xhr.ts 第 52 行),fetch 适配器以原始 ReadableStream 作为 response.data 返回。paramsSerializer 全局默认与 encode 内建 qs 级功能:数组序列化 indexes 三态(true→foo[0]、null→foo、缺省→foo[])已对齐官方 v1,但不支持官方 qs 的更多选项。基于以上接口结构,向本库新增一个配置项(以假想的 retry: number 为例)需要按顺序触碰五处,缺一即形成"半成品特性":①src/types/index.ts 的 AxiosRequestConfig 增加字段声明与 JSDoc(得益于末尾索引签名,不做此步也能编译通过,但会失去类型提示,属必做项);②若需要默认值,在 src/defaults.ts 补默认值,并确认 mergeConfig 的默认策略(defaultStrat)是否满足合并语义——对象型且需深合并的要把键名加进 stratKeysDeepMerge;③在恰当的层消费:链路级语义放 dispatchRequest.ts,传输级语义放具体适配器(多端都要生效就得三处都写,或下沉为共享 helper);④在 src/types/index.ts 的 AxiosStatic 静态面与 smoke-dist.cjs 同步登记(仅当新特性引入新的静态导出时);⑤补测试:行为用例进 test/ 根或 test/core/,纯函数用例进 test/helpers/,并确认覆盖率门槛仍然达标。回看既有特性的落地痕迹(如 withXSRFToken 贯穿 types → defaults 未设默认 → xhr/fetch 两适配器消费 → test/xsrf.spec.ts 与 test/advancedConfig.spec.ts 双覆盖),正是这套清单的完整体现。