Back

使用 OpenAPI 和 TypeScript 构建类型安全的 API 客户端

使用 OpenAPI 和 TypeScript 构建类型安全的 API 客户端

每个前端开发者都遇到过这种情况:从 API 获取数据,访问一个应该存在的字段,结果在运行时得到 undefined。TypeScript 本应防止这种情况。问题在于 response.json() 返回 any,因此编译器无法进行检查。本指南将向你展示如何正确解决这个问题——直接从 OpenAPI 规范生成类型,并使用它们在 TypeScript 中构建类型安全的 REST 客户端。

核心要点

  • 手动编写的 TypeScript 接口会与实际 API 产生偏差,造成虚假的安全感,最终导致运行时错误。
  • OpenAPI 代码生成直接从 API 规范生成类型,使前端和后端保持同步。
  • 使用 openapi-typescript 配合 openapi-fetch 实现轻量级的、基于 fetch 的设置,或使用 Orval 生成 TanStack Query hooks 和完整的客户端 SDK。
  • 在 CI 或构建脚本中自动化类型生成,确保类型永不过时。
  • 将生成的类型与 Zod 等运行时验证器配合使用,为关键端点提供编译时安全之外的额外保障。

为什么手动类型定义会失效

简单粗暴的修复方法是手写接口:

// ❌ 编译通过,但 TypeScript 只是在相信你
const data = (await response.json()) as User

这能给你自动补全,但一旦后端发生变化,它就变成了谎言。你的类型与现实产生偏差,又回到了运行时意外的老路上。

更好的方法:从真实来源——你的 OpenAPI 规范——生成类型。

OpenAPI TypeScript 代码生成工作流

典型的工作流如下:

  1. 从 OpenAPI 规范开始(YAML 或 JSON,托管或本地)
  2. 运行生成器生成 TypeScript 类型或完整客户端
  3. 将这些类型导入到应用程序代码中

这使你的前端自动与后端保持同步。当规范发生变化时,重新生成即可,TypeScript 会准确告诉你哪里出了问题。

选择你的方案:仅类型 vs 完整客户端 SDK

OpenAPI TypeScript 代码生成有两种主要策略,正确的选择取决于你的项目。

方案工具最适合
生成类型,自带 fetchopenapi-typescript + openapi-fetch轻量级、基于 fetch 的项目
生成完整客户端 SDKOrvalOpenAPI Generator希望使用现成 hooks 和客户端的团队

仅类型生成保持打包体积小,让你控制 HTTP 层。完整 SDK 生成节省连接时间,但会增加更多需要维护的生成代码。

OpenAPI 3.0 vs 3.1:大多数工具对 OpenAPI 3.0 支持良好。OpenAPI 3.1 的支持程度各不相同——在假设完全兼容之前,请查看生成器的文档。

方案 1:openapi-typescript 配合 openapi-fetch

这是最小运行时路径。从规范生成类型,然后使用 openapi-fetch 作为原生 Fetch API 的轻量级、完全类型化的包装器。

npx openapi-typescript ./openapi.yaml -o ./src/api/types.ts
npm install openapi-fetch
import createClient from "openapi-fetch"
import type { paths } from "./api/types"

const client = createClient<paths>({ baseUrl: "https://api.example.com" })

// 路径、参数和响应都经过类型检查
const { data, error } = await client.GET("/users/{id}", {
  params: { path: { id: 123 } },
})

// TypeScript 准确知道 `data` 的结构
console.log(data?.email)

路径拼写错误、参数类型错误和响应结构不匹配都会变成编译错误。最小的运行时开销,只有一个小型运行时依赖(openapi-fetch)。

方案 2:使用 Orval 生成完整客户端

Orval 生成类型化的 API 函数,更重要的是,可以直接从规范输出 TanStack Query hooks。当你希望数据获取逻辑自动处理时,这非常有用。

npm install orval --save-dev

配置 orval.config.ts 指向你的规范并选择输出模式(fetch、axios 或 react-query)。然后 Orval 会生成像 useGetUsers() 这样的函数,内置完整的类型安全。

这种方法会增加更多生成的代码,但对于大型 API,它能显著减少样板代码。

保持类型同步

只有持续重新生成,OpenAPI TypeScript 客户端生成的真正价值才能体现。将生成步骤添加到开发工作流中:

{
  "scripts": {
    "generate:api": "openapi-typescript ./openapi.yaml -o ./src/api/types.ts"
  }
}

在 CI 中运行,或在本地监听规范变化。一些团队提交生成的文件;另一些团队在每次构建时重新生成。两种方式都可行——只要让它自动化即可。

你仍需处理的问题

生成的类型通常只提供编译时安全。运行时验证需要额外的工具,如 Zod 或生成器插件。对于关键端点,将生成的类型与 Zod 配合使用,在运行时验证响应,在后端 bug 影响 UI 之前捕获它们。

总结

OpenAPI TypeScript 代码生成是你可以对前端代码库做出的最高杠杆率改进之一。选择 openapi-typescript 配合 openapi-fetch 实现轻量级设置,或者如果你想要生成的查询 hooks,选择 Orval。无论哪种方式,你都不再需要手写类型,不再猜测响应结构,让编译器做它本该做的工作。

常见问题

是的。openapi-typescript 同时支持 OpenAPI 3.0 和 3.1 规范,尽管某些 schema 特性可能需要使用你的特定生成器和规范进行测试。升级规范版本后,务必验证生成的输出。

当然可以。生成的类型为你提供编译时安全,而 Zod 在运行时验证数据。你可以定义与生成类型相对应的 Zod schemas,并通过它们解析 API 响应。这能捕获编译器无法检测到的、后端返回意外数据的情况。

两种方法都可行。提交生成的文件使构建更快,并允许你在 pull request 中审查类型变化。在每次构建时重新生成确保类型始终是最新的,但会增加构建步骤依赖。选择适合你团队工作流和 CI 设置的方式。

Orval 专为 TypeScript 构建,只需最少配置即可输出 TanStack Query hooks、Axios 客户端或 fetch 函数。OpenAPI Generator 支持多种语言,但生成的 TypeScript 输出更冗长。对于专注前端的团队,Orval 通常需要更少的自定义即可获得简洁、惯用的代码。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay