vite-plugin-api-types-gen
v0.1.1
Published
根据 Swagger/OpenAPI 文档或运行时 API 响应,自动生成 TypeScript 类型定义的 Vite 插件
Maintainers
Readme
vite-plugin-api-types-gen
English | 简体中文
根据 Swagger / OpenAPI 文档 或 浏览器运行时真实响应,自动生成 TypeScript 类型定义的 Vite 插件。 无需手写接口类型,嵌套对象、数组与树形结构会被自动拆分为可单独引用的命名类型。
🌟 核心特性
- 🚀 双数据源:既支持 Swagger/OpenAPI 文档(JSON / YAML),也支持在浏览器运行时自动捕获真实响应
- 🧩 嵌套拆分:嵌套对象、数组元素自动拆为独立
interface,可单独import type引用 - 🌲 树形自引用:
children/nodes等递归字段自动生成自引用类型(如MenuTreeNode.children?: MenuTreeNode[]) - 🧾 请求体类型:请求体为对象时同样生成类型(如
CreateUserReq) - 📁 单文件 / 多文件:
per-api每个接口一个 ts 文件并生成index.ts聚合导出;single全部写入一个文件 - ♻️ 双重去重:单次解析内按 Schema 结构指纹去重,
per-api模式下跨文件重复类型自动import type复用 - 🧠 LRU 缓存:避免多次生成相同类型,自动限制内存占用
- 🗑️ 空文件不落盘:接口没有可生成的类型时不会保留空文件,并会清理陈旧空文件
- ✋ 捕获手动开启:响应捕获默认关闭,需运行时手动开启,且同一接口默认只生成一次,不会每次请求都生成
- 🎯 灵活过滤:
include/exclude同时支持字符串包含匹配与正则 - 🛡️ 无侵入:通过克隆响应解析数据,不影响原请求执行
📦 安装
# npm
npm install vite-plugin-api-types-gen --save-dev
# yarn
yarn add vite-plugin-api-types-gen -D
# pnpm
pnpm add vite-plugin-api-types-gen -D尚未发布到 npm 时,可使用本地路径安装:
{ "devDependencies": { "vite-plugin-api-types-gen": "file:../vite-plugin-api-types-gen" } }
🚀 快速开始
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { apiTypesGen } from 'vite-plugin-api-types-gen'
export default defineConfig({
plugins: [
vue(),
apiTypesGen({
outDir: 'src/api-types', // 类型输出目录(默认 src/api-types)
outputMode: 'per-api', // 每个接口一个 ts 文件 + index.ts 聚合导出
recursiveKeys: ['children'], // 树形结构识别字段
swagger: {
enabled: true,
// 支持 http(s) 地址,也支持本地文件(JSON / YAML 均可)
url: 'http://localhost:8080/v3/api-docs',
exclude: ['/actuator'],
typeNameSuffix: 'Ret', // GET /api/menu/list -> GetMenuListRet
},
}),
],
})npm run dev 启动时会自动拉取文档并生成类型到 outDir。
完整的可运行宿主示例(Vue 3)见
examples/host。
⚙️ 配置项说明
顶层配置
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| outDir | string | src/api-types | 类型文件输出目录(相对于项目根目录) |
| outputMode | 'per-api' \| 'single' | 'per-api' | per-api:每个接口一个文件;single:全部写入 api-types.ts |
| barrel | boolean | true | per-api 模式下是否生成 index.ts 聚合导出文件 |
| allPropertiesOptional | boolean | false | 是否把所有属性都标记为可选 |
| recursiveKeys | string[] | ['children','child','nodes','items'] | 递归/树形结构识别字段名,命中且结构与父级一致时生成自引用类型 |
| runOnBuild | boolean | false | vite build 阶段是否也执行一次 Swagger 生成 |
| lru.maxSize | number | 200 | LRU 缓存条目上限 |
| logger | boolean | true | 是否输出插件日志 |
swagger 数据源
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| enabled | boolean | false | 是否启用 Swagger/OpenAPI 生成 |
| url | string | - | 文档地址(http(s)://)或本地文件路径,支持 JSON 与 YAML |
| include | (string \| RegExp)[] | [] | 只处理命中的接口路径,为空表示全量 |
| exclude | (string \| RegExp)[] | [] | 排除命中的接口路径(优先级高于 include) |
| typeNamePrefix | string | '' | 生成根类型名的前缀 |
| typeNameSuffix | string | 'Ret' | 生成根类型名的后缀 |
| headers | Record<string, string> | {} | 拉取远程文档时携带的请求头(如鉴权 Token) |
capture 响应捕获
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| enabled | boolean | false | 是否初始开启采集(默认关闭,需运行时手动开启) |
| include | (string \| RegExp)[] | [] | 需要采集的接口路径,为空表示全量 |
| exclude | (string \| RegExp)[] | [] | 排除采集的接口路径 |
| overwrite | boolean | false | 已采集过的接口是否允许覆盖重新生成 |
| collectPath | string | /__api-types-gen/collect | 浏览器上报采集数据的地址 |
| typeNamePrefix | string | '' | 生成根类型名的前缀 |
| typeNameSuffix | string | 'Ret' | 生成根类型名的后缀 |
| sampleSize | number | 50 | 数组元素采样数量,用于推断数组元素类型 |
🎨 类型生成规则
1. 根类型命名
优先使用接口的 operationId,没有 operationId 时使用「请求方法 + 路径」(路径参数 {id} 会被忽略):
| 接口 | 生成根类型 |
| --- | --- |
| GET /api/menu/list(operationId: getMenuList) | GetMenuListRet |
| POST /users(无 operationId) | PostUsersRet |
| GET /users/{id}(无 operationId) | GetUsersRet |
最终名称 = typeNamePrefix + 基础名 + typeNameSuffix,Vue / TS 文件中可直接引用。
2. 基础类型映射
| JSON Schema 类型 | TypeScript 类型 |
| --- | --- |
| string | string |
| number / integer | number |
| boolean | boolean |
| null | null |
| object(有 properties) | 递归拆分为独立 interface |
| array | T[](元素为对象时单独成类型) |
| 未声明类型 / 空对象 | Record<string, unknown> / unknown |
3. 嵌套与复杂结构
- 嵌套对象 / 数组元素对象会被拆分为独立
interface,支持单独引用; - 子类型名取自字段名(PascalCase),数组字段自动单数化(
list→List、children→Child); enum生成字面量联合类型,如export type Statu = 0 | 1;oneOf/anyOf生成联合类型,成员按XxxItem1、XxxItem2命名;allOf会合并为一个对象类型;- 结构完全相同的 schema 只定义一次,重复出现时复用同一类型。
4. 请求体类型
请求体为对象类结构时(OpenAPI 3 的 requestBody 或 Swagger 2 的 parameters[in=body])会额外生成请求体类型,
根名称为「基础名 + Req」,并与该接口的响应类型写在同一个文件中:
// create-user-ret.ts
export interface User { /* ... */ }
export interface CreateUserRet { code?: number; data?: User }
export type Statu = 0 | 1
export interface CreateUserReq {
username: string
email: string
password: string
age?: number
status?: Statu
}📁 输出产物
per-api(默认)
每个接口一个文件,文件名由根类型名转为 kebab-case,并在 outDir/index.ts 生成聚合导出:
src/api-types/
├── get-menu-list-ret.ts # export interface MenuTreeNode / GetMenuListRet
├── get-user-info-ret.ts # export interface UserInfo / GetUserInfoRet
├── login-ret.ts # import type { UserInfo } from './get-user-info-ret'
└── index.ts # export type { ... } from './xxx'同一个接口的响应与请求体类型会写在同一个文件里,可直接被 .vue 文件 import type 引用。
若某个接口没有可生成的类型,则不会产生对应文件。
single
所有类型汇总写入 outDir/api-types.ts,按类型名去重追加,不会重复定义。
🛰️ 响应自动捕获
在配置中写了 capture 字段后,插件会在 dev 环境 注入采集脚本(默认不采集,不会每次请求都生成类型)。
脚本会拦截 fetch 与 XMLHttpRequest,克隆响应并解析 JSON,不影响原请求。
apiTypesGen({
capture: {
enabled: false, // 默认关闭:需运行时手动开启
include: ['/api'],// 只采集 /api 开头的接口
overwrite: false, // 同一接口只生成一次
},
})运行时在浏览器控制台手动控制:
window.__API_TYPES_GEN__.enable() // 开启采集
// 触发需要生成类型的接口(每个接口默认只采集一次)
window.__API_TYPES_GEN__.status() // 当前是否开启
window.__API_TYPES_GEN__.captured() // 查看已采集的接口列表
window.__API_TYPES_GEN__.disable() // 关闭采集
window.__API_TYPES_GEN__.reset() // 清空记录,允许重新采集采集到响应后会写入 outDir 并自动刷新页面。若希望换一个接口重新采集,先调用 reset() 再重新请求即可。
⚠️ 暂不支持流式接口
当前版本不支持流式(Streaming)接口的类型生成,包括:
- 响应采集侧:SSE(
text/event-stream)以及基于ReadableStream的分块响应(如大模型 Chat Completions 流式返回)无法被解析,因为采集依赖克隆响应体并整体JSON.parse,分块内容并非完整 JSON,这类接口会被自动跳过; - 文档侧:Swagger/OpenAPI 中把响应声明为
text/event-stream等非 JSON 媒体类型的接口,不参与类型生成。
也就是说,POST /api/paas/v4/chat/completions 这类流式接口不会生成类型文件(而非生成空文件)。后续版本会评估支持,届时可参考文档或采集到的**非流式(stream: false)**响应作为替代方案。
🚫 过滤接口示例
include / exclude 支持字符串包含匹配,也支持正则:
apiTypesGen({
swagger: {
enabled: true,
url: 'http://localhost:8080/v3/api-docs',
include: ['/api/'], // 只处理 /api/ 下的接口
exclude: [/\/actuator/, /\/internal/], // 排除监控与内部接口
},
capture: {
include: [/^\/api\//],
exclude: ['/api/health', '/api/metrics'],
},
})🧩 在 Vue 中使用
<script setup lang="ts">
// 从聚合入口统一引用
import type { MenuTreeNode, GetMenuListRet } from '@/api-types'
// 也可按接口文件单独引用
import type { GetMenuListRet } from '@/api-types/get-menu-list-ret'
const list = ref<MenuTreeNode[]>([])
const data = ref<GetMenuListRet>()
</script>需要
@别名指向src,例如resolve.alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }。
🛠️ 常见问题
1. 类型没有生成?
- 检查
swagger.enabled是否为true、url是否正确; - 本地文件路径相对于项目根目录;YAML 与 JSON 均支持;
- 确认接口没有被
include/exclude过滤掉; - 若在构建阶段也需要生成,请设置
runOnBuild: true。
2. 生成后类型提示不生效?
- 执行 VS Code 命令
TypeScript: Restart TS Server(Ctrl+Shift+P); - 确认
tsconfig.json的include覆盖了outDir目录; - 打开生成的
*.ts文件确认类型已正确写入。
3. 采集没有生效?
capture.enabled默认是false,需要在浏览器控制台调用window.__API_TYPES_GEN__.enable();- 确认请求路径命中
capture.include且未被capture.exclude排除; - 同一接口默认只采集一次,重新采集请先
reset()。
4. per-api 模式下某个文件被删除了?
该接口没有生成任何类型(例如 204 No Content、响应与请求体都没有结构),属于预期行为,不会保留空文件。
5. 流式接口为什么没有生成类型?
流式(Streaming)接口暂不支持:SSE / text/event-stream / ReadableStream 分块响应无法被整体解析为 JSON,因此不会生成类型文件。
可改用该接口的**非流式(stream: false)**响应,或直接依据 Swagger/OpenAPI 文档生成。
📋 兼容性
- Vite 版本:
^4.0.0 || ^5.0.0 || ^6.0.0 - Swagger / OpenAPI:Swagger 2.0 与 OpenAPI 3.x 的常见结构(
$ref/allOf/oneOf/anyOf/enum) - Node 版本:Node 16+
- 暂不支持:流式接口(SSE /
text/event-stream/ 分块ReadableStream响应)的类型生成
📄 许可证
MIT License
✨ 最佳实践
- 捕获模式仅在开发环境临时开启,生成完成后建议关闭;
- 以 Swagger/OpenAPI 文档为主数据源,捕获模式用于文档缺失或字段不准的接口兜底;
- 生成的类型文件可提交到代码仓库,供团队共享;
- 配置
recursiveKeys以匹配后端真实的树形字段名,避免树形结构生成错误; - 使用
exclude过滤掉监控、健康检查等无意义的接口。
