资讯详情

用AI从Swagger自动生成TypeScript接口层:完整实操指南

📅 2026/9/19 6:55:55 | 华诺云谱 👁 阅读
用AI从Swagger自动生成TypeScript接口层:完整实操指南
先交代一下背景。我做前端差不多十年这十年里最烦的一件事就是给后端接口手写 TypeScript 类型和请求函数。后端把 Swagger 文档一贴剩下的活全是前端的几十上百个接口每个接口的入参、出参、错误码全得一个个对着看稍不留神字段名就写错了一调接口直接 400。最近一年多我开始用 AI 做这件事实测下来手写接口层这个环节确确实实可以砍掉了。这篇文章不聊虚的就讲清楚我踩过的坑和跑通的方案怎么从后端 Swagger 文档里拿到原始定义怎么把它切成 AI 能消化的片段怎么写提示词让 AI 输出可直接用的 TypeScript 类型和请求函数以及项目里落地时那些文档里不会写的注意事项。适合被接口层折磨过的前端也适合团队里想提效、想统一代码风格的技术负责人。1. 后端 Swagger 文档是前端接口层最理想的“原材料”1.1 Swagger 文档里到底埋了哪些信息先别急着让 AI 干活得先搞清楚 Swagger 文档本身是什么。Swagger 这个词严格来说指的是用 OpenAPI 规范描述后端接口的一套工具链。我们现在看到的大多数 Spring Boot 项目用的是 springfox 或 springdoc 生成 Swagger 2.0 / OpenAPI 3.0 文档暴露出来的 API 描述其实就是一个 JSON 文件里面有后端所有接口的路径、请求方法、参数定义、响应结构和数据模型。一个典型的 OpenAPI 3.0 文档核心就两块paths记录每个接口的 URL、HTTP 方法、入参path、query、header、body、出参。components.schemas记录所有复用的数据模型比如用户对象、订单对象、分页对象。举个例子后端可能暴露一个{ /api/users: { get: { tags: [用户管理], operationId: pageUsers, parameters: [ { name: page, in: query, schema: { type: integer, format: int32 } }, { name: size, in: query, schema: { type: integer, format: int32 } } ], responses: { 200: { description: OK, content: { application/json: { schema: { $ref: #/components/schemas/PageResult } } } } } } } }光看这一段一个熟练前端能写出对应的请求函数和 PageResult 接口。但问题是项目大了以后这样的 path 可能有几百个schema 可能有几十上百个手写一遍两天就过去了写完了后端接口一改又来一遍。1.2 为什么人工手写接口层容易翻车不是前端不细心而是手写接口层这事儿天然有坑。第一Swagger 里integer/int64映射到 TypeScript 只能是number但实际项目里后端常把 Long 类型主键序列化成字符串传给前端如果前端只按文档写number数据精度就悄悄丢了。第二后端字段命名风格和前端不一致有的叫created_at有的叫createdAtAI 还能理解语境人工照着文档抄就容易漏改。第三接口更新频率高今天加个status明天删个remark手写的类型文件永远是滞后的。所以问题不在于“要不要生成”而在于“怎么生成得更聪明”。传统代码生成器能解决一部分但它解决不了命名可读性和业务含义理解的问题这时候 AI 的价值就出来了。2. AI 转译方案的整体设计我为什么从传统代码生成器转投 AI2.1 传统工具 swagger-typescript-api / openapi-generator 能干什么传统方案里我最早用的是 swagger-typescript-api后来也试过 openapi-generator。它们很成熟能一条命令把整个 Swagger JSON 变成一整个.ts文件或者按模块拆文件生成结果非常稳定CI 里也可以跑。对于“能跑就行”的团队这其实是一个不差的选择。但它有几个让我难受的点。最典型的是命名和类型结构它生成的类型非常“字面化”后端 schema 叫UserVO前端就给你生成UserVO完全不考虑前端代码里的语义习惯如果后端把PageResult和PageResultDTO都定义出来生成结果就同时存在两个几乎一模一样的接口前端调的时候还得自己确认用哪个。模板也很死板想改成团队自己的请求封装、错误处理风格要折腾模板配置付出的时间比手写还多。2.2 AI 相比传统工具的核心优势在哪我用 AI 重新做一遍之后感受最大的是三点。第一AI 能理解“语义”而不是只有“结构”。同一个 schemaAI 看到createTime字段会识别成时间字符串看到枚举值status: 0/1/2会结合字段注释补上每个取值的含义。传统工具只会生成status: number后面的人还得翻接口文档猜这个 0 和 1 到底什么意思。第二AI 能顺手补齐注释和错误码说明。Swagger 文档里有很多description字段传统代码生成器默认不生成AI 却可以把这些描述整理成 JSDoc哪个字段必传、哪个字段只在特定状态下返回一目了然。第三AI 输出的是“可读的业务代码”不是“机器生成的中间产物”。它生成的类型和请求函数拿给团队其他前端 review基本不需要额外解释风格和手写的一致。2.3 什么时候用 AI什么时候还是老老实实用生成器我也不是无脑鼓吹所有阶段都用 AI。我现在的选择逻辑是场景推荐方案原因项目刚开始、接口频繁变动AI 按模块生成灵活改动成本低AI 能跟着接口调整老项目接口稳定、只需要一次性导入传统生成器一条命令搞定生成结果确定性强团队有强制代码风格、要 CI 自动化传统生成器 模板定制可重复执行AI 输出有随机性需要生成结果带注释、便于新人阅读AI语义理解好注释质量高接口文档涉及敏感字段本地化 AI 或人工核对防止把内部接口定义发到外部这里提醒一句AI 生成结果有随机性所以在正式项目里我建议把 AI 当成“高级代码生成器”出来的代码必须过一遍 TypeScript 编译检查和人工 review。后面会细讲怎么控制这个不确定性。3. 实操第一步从 Swagger 文档中抽取“干净原料”喂给 AI3.1 先拿到 Swagger JSON通常后端会在测试环境暴露一个接口地址Spring 系一般是/v2/api-docs或/v3/api-docs直接 curl 就能下载到 JSONcurl http://192.168.1.100:8080/v3/api-docs -o swagger.json如果文档是 YAML 格式或者后端给的是一个导出文件可以先转成 JSON。我经常用 Python 写一个小脚本做预处理把完整的 Swagger 文档按 tag 拆成多个小文件方便分模块喂给 AI。注意一个非常关键的问题生产环境的 Swagger 接口一定要关掉或加权限不然接口信息等于公开给所有人风险极高这个我们放到常见问题里详细说。3.2 最忌讳的行为一次性把整个 Swagger JSON 全塞给 AI这是很多新手最想做的事情把swagger.json直接拖进对话框然后一句“把接口转成 TS 吧”。结果就是大模型的上下文窗口吃满输出到一半截断就算不截断它也会因为信息太多而“偷工减料”前面几个接口生成得很认真后面的接口变成无意义的重复结构。我更推荐按模块拆。一般后端在定义接口时都会加 tag比如“用户管理”“订单管理”“商品管理”那么我会按 tag 作为切分维度。每个模块单独生成一个类型文件和一个 API 文件这样 AI 每次只需要盯着一个业务模块精度和一致性会好非常多。3.3 预处理脚本怎么处理 $ref 和多余字段Swagger 文档里大量使用$ref引用比如上面的/api/users响应引用了PageResult。直接把原始 JSON 塞给 AI它会遇到很多“这个引用指向哪里”的问题虽然它大概率能推导但推导多了就容易错。我的做法是用脚本把涉及到的$ref局部展开或者至少把引用的目标 schema 一并提取放在同一个片段里。以下是一个简单的 Python 预处理脚本我实际项目中就在用你可以根据自己的 Swagger 结构调整import json def load_swagger(file_path): with open(file_path, r, encodingutf-8) as f: return json.load(f) def extract_by_tag(spec, tag_name): 按 tag 提取 paths同时收集相关的 schemas components spec.get(components, {}).get(schemas, {}) paths spec.get(paths, {}) result_paths {} referenced_schemas set() for path, methods in paths.items(): for method, op in methods.items(): if not isinstance(op, dict): continue tags op.get(tags, []) if tag_name in tags: result_paths[path] methods # 找到这个 path 里出现的所有 $ref去重加入集合 _collect_refs(op, referenced_schemas) extracted_schemas {} for schema_name in referenced_schemas: if schema_name in components: extracted_schemas[schema_name] components[schema_name] return {paths: result_paths, schemas: extracted_schemas, tag: tag_name} def _collect_refs(node, ref_set): 遍历 dict/list找出所有 $ref 字符串 if isinstance(node, dict): if $ref in node: ref node[$ref].split(/)[-1] ref_set.add(ref) for v in node.values(): _collect_refs(v, ref_set) elif isinstance(node, list): for item in node: _collect_refs(item, ref_set) # 用法 spec load_swagger(swagger.json) user_module extract_by_tag(spec, 用户管理) with open(user_module.json, w, encodingutf-8) as f: json.dump(user_module, f, ensure_asciiFalse, indent2)这一步做完每个模块的 JSON 文件控制在几十到一两百行再扔给 AI 就不会“消化不良”了。4. 实操第二步设计提示词让 AI 输出可用的 TypeScript 类型4.1 提示词模板把规则和示例一次说清AI 生成代码的质量很大程度上取决于提示词。我的提示词里会包含三部分输入原料、输出格式要求、一个 few-shot 示例。这样可以极大减少 AI 的自由发挥空间。下面是一份我常用且效果稳定的提示词模板你是资深 TypeScript 架构师。现在给你一份后端 OpenAPI 模块定义片段包含 paths 和 schemas。 请为前端生成一个 TypeScript 文件要求 1. 为每个 schema 生成对应的 interface字段类型映射遵循 - string - string - integer / int64 / number - number - boolean - boolean - array - 数组类型 - 可空字段统一用 optional?表示值为 null 时也接受 2. 为每个 GET 请求生成一个请求函数函数名使用小驼峰如 pageUsers 参数对象为 params包含 path 参数、query 参数 返回类型为 the response schema interface。 3. 在字段上方写 JSDoc 注释内容取自已给 schema 的 description enum 字段要列出所有取值及含义。 4. 只输出 TypeScript 代码不要输出解释和 markdown 代码块标记。这里的“只输出代码”很重要。如果不加这句AI 每次会输出“我先分析一下”之类的废话后处理时要剥掉额外内容非常烦。4.2 Swagger 类型到 TypeScript 类型的映射表为了让 AI 和团队保持统一我沉淀了一张映射规则表每次提示词里也会附上避免模型自己发挥OpenAPI 类型OpenAPI formatTypeScript 类型string- / uuid / emailstringstringdate / date-timestring建议配合 dayjs 使用integerint32 / int64numbernumberfloat / doublenumberboolean-booleanarray-T[]object-interface 或 Recordstring, unknownschema 中的 $ref-引用对应的 interface有个细节容易踩坑后端返回数值型 ID 时int64 用 JSON 序列化后有时会变 stringAI 只看 OpenAPI 定义不会知道这件事。所以我在提示词里会补一句“如果字段名以 Id/No 结尾且类型为 integer生成类型优先用 string因为后端可能返回字符串 ID”。这是应对现实接口的经验规则传统工具做不到这么灵活。4.3 enum、allOf、oneOf 这三种结构怎么处理这三个是 Swagger 转 TypeScript 时最容易翻车的地方。enum 字段我推荐生成字符串联合类型而不是数字枚举。AI 生成的效果大概是/** * 用户状态 * 0: 禁用, 1: 启用, 2: 待审核 */ export type UserStatus 0 | 1 | 2;比enum UserStatus { ... }更直观也更容易和后端交互。allOf 表示“继承”OpenAPI 里经常用来做“基础模型 扩展字段”AI 应该生成 interface 继承。oneOf 表示“可能是 A 也可能是 B”应该对应联合类型。这两类数据结构每次我看到提示词里没明确要求时AI 经常把它拍平成一个大 interface虽然没报错但信息丢了。所以我会在提示词里专门写遇到 allOf 时生成 interface 并用 extends 实现继承 遇到 oneOf 时生成联合类型。4.4 让 AI 把注释变成团队的知识库我比较看重注释生成因为这决定接口层代码是“给人看的”还是“只给机器跑的”。AI 能把description里的中文说明整理成 JSDoc例如/** * 分页查询用户列表 * param params.page 页码从 1 开始默认 1 * param params.size 每页条数默认 10 * param params.status 用户状态筛选为空则不筛选 */ export function pageUsers(params: PageUsersParams): PromisePageResultUserVO { return request.get(/api/users, { params }); }这样的代码别的前端接手时不用再去翻后端文档效率提升是实打实的。5. 实操第三步生成可落地的请求函数不只是类型5.1 先准备团队自己的请求基础封装要让 AI 生成的请求函数真正能跑得先给它一个“底座”。我团队里有一个request.ts基础封装统一了 baseURL、超时时间、token 注入、HTTP 错误码提示。AI 生成请求函数时只需要调用一个统一的request对象不用关心 axios/fetch 细节。这一步建议自己写不要让 AI 代劳因为请求基础封装涉及 token 刷新、错误拦截和业务码判断属于每个团队的差异化逻辑。AI 生成请求函数时我会在提示词里塞入这个模板的最小示例import { request } from /utils/request; // AI 生成的请求函数格式示例 export function pageUsers( params: PageUsersParams ): PromisePageResultUserVO { return request.get(/api/users, { params }); }5.2 解析 path 参数、query 参数和 body 参数的差异Swagger 里的参数有三种位置pathURL 路径里、query问号后、body请求体 JSON。AI 必须能识别三种场景并生成不同代码。path 参数的典型接口是把 ID 放在 URL 里/** * 根据 ID 获取用户详情 * param id 用户ID */ export function getUserById(id: string): PromiseUserDetailVO { return request.get(/api/users/${id}); }query 参数走params对象body 参数走data。为了不让 AI 混淆我在提示词中会强调in: path参数拼进 URL 模板in: query参数放到paramsrequestBody内容放到data。大多数条件下靠着字段名就能判断对但明确写出来生成结果的稳定性能提高不少。如果团队封装里用的不是 axios而是 fetch 封装可以在模板里微调总之一定要把模板给 AI 看不然它默认生成的格式放到项目里不一定能直接跑。5.3 后端统一返回包裹结构怎么处理现实里绝大多数后端接口并不是直接把业务数据返回而是包了一层{ code: 0, message: success, data: ... }。如果 Swagger 文档里每个接口的响应都写的是这个包裹结构前端就遇到一个问题请求函数应该返回data还是整个包裹我现在的统一规则是请求函数内部解包业务代码只拿data。所以提示词里会要求 AI 生成请求函数时返回类型写泛型传入的业务类型例如PromiseUserDetailVO同时忽略包裹层的信息。这需要 AI 对文档做一层“语义判断”传统生成器要配很多模板配置才能做到AI 直接理解。5.4 生成之后的产物长什么样我拿一个真实项目里的片段给你感受一下。AI 处理完“用户管理”模块后文件user.ts长这样// 类型定义 /** 分页参数 */ export interface PageParams { page?: number; size?: number; } /** 用户信息 */ export interface UserVO { id: string; username: string; nickname?: string; avatar?: string; email?: string; /** 用户状态 0: 禁用 1: 启用 2: 待审核 */ status: 0 | 1 | 2; createTime: string; updateTime: string; } /** 分页结果 */ export interface PageResultT { list: T[]; total: number; page: number; size: number; } // 请求函数 /** * 分页查询用户列表 * param params.page 页码从 1 开始 * param params.size 每页条数 * param params.status 用户状态筛选 */ export function pageUsers( params: PageParams { status?: UserVO[status] } ): PromisePageResultUserVO { return request.get(/api/users, { params }); } /** * 获取用户详情 * param id 用户ID */ export function getUserById(id: string): PromiseUserVO { return request.get(/api/users/${id}); }这样的文件即使 AI 生成完毕人工 review 起来也毫无压力因为风格就是团队手写会采用的样子。6. 常见问题与排查技巧实录6.1 Swagger 文档根本拿不到 / 拿到的是空壳后端环境没开 Swagger、接口在网关后面需要额外 header 才能访问、或者文档只放到了某个内网地址这些我都遇到过。排查思路是先用 curl 直接访问地址如果返回 401/403需要找后端要测试环境的鉴权方式。如果拿到了 JSON 但paths是空的多半是后端配置了只扫描部分 controller这时候只能找后端协调。提醒一句如果你在浏览器能轻松打开swagger-ui.html查看所有接口那么要警惕 Swagger 接口信息对外暴露的风险。接口未授权访问是安全扫描里的常见问题生产环境一定要关掉或加权限认证不能把内部接口结构直接裸奔在公网。6.2 生成的类型和后端实际返回对不齐最典型的情况是后端代码改了Swagger 文档没重新生成导致 AI 基于旧文档输出前端调用新字段就报 undefined。这不是 AI 的问题是文档和实现不同步的问题。我建议在项目里加一道运行时校验兜底用 zod 或者 io-ts 给关键接口写一个 response schema 校验能在开发环境直接发现字段不匹配。另外每次后端升级接口回到本文第 3 节的预处理流程重新跑一遍 AI 生成我一般会写一个自动化脚本新文档一发布生成结果自动交给前端做 diff review。6.3 AI 生成结果不稳定怎么办AI 有一个“老毛病”同样的提示词运行两次结果不完全一样。为了降低不确定性我给自己定了三条铁律提示词里给足够具体的输出规范越具体越好不给它自由发挥的空间。一次生成以后先用tsc --noEmit过一遍任何类型错误立即改完再审。如果某个复杂接口反复生成不对就把那个接口单独拆出来写一个示例让 AI 模仿示例输出。我试用过好几个模型做这件事整体感受是上下文窗口越大、越稳定的模型生成效果越好。本地部署还是调用 API 取决于团队数据隐私要求如果接口数据里有敏感信息更推荐用本地模型或私有化部署不要把内部文档直接发到外部。6.4 大型项目怎么分步落地对于有几十个模块的老项目不建议一次性全量生成替换。我建议“增量替换、风险隔离”新接口、新页面从第一天就按 AI 生成方案走。老接口只在“要改这个模块”的时候再重新生成生成后和手写代码做 diff。每次生成完重点 review类型名称是否和团队规范一致、请求函数是否正确拆包、分页参数是否对齐、枚举值是否完整。这样滚动推进大概两三个迭代之后整个项目的接口层就被“洗”成统一风格了而且没人需要再对着文档手敲类型。7. 最后分享一点个人体会做完这件事之后我最深的感受是手写接口层的活儿确实不再需要投入大量人力了但不代表前端可以完全不看接口文档。恰恰相反前端需要更清楚地理解接口的数据结构和业务含义才能判断 AI 生成的对不对。AI 把我们从重复劳动里解放出来同时也把“判断力”变成了更值钱的能力。我现在的流程是后端文档更新我先把 JSON 跑一遍预处理脚本然后按模块喂给 AI生成以后人肉过一遍 diff再本地跑一遍 tsc最后交给测试。整个流程快的时候十几分钟慢的时候半小时和以前动辄一两天的手写相比提升是量级的。如果你也想在团队里做同样的事我建议第一步别追求完美先拿一个中小模块试水对比一下手写代码和 AI 生成代码的差异再决定要不要全面铺开。工具链这个东西适合自己团队的才是最好的。希望这篇实操记录能帮你少踩一些坑。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。