跳到主要内容

openapi-typescript-codegen

@hey-api/openapi-tsopenapi-typescript-codegen 的活跃维护分支,支持从 OpenAPI/Swagger 文档生成 TypeScript 类型定义和 API 客户端代码

工具说明

与 openapi-typescript-codegen 的关系

openapi-typescript-codegen 项目已不再维护,官方建议迁移到 @hey-api/openapi-ts,这是原项目的活跃分支,修复了原项目的问题并持续更新。

核心特性

  • 灵活配置:可以只生成类型,也可以生成服务代码
  • 类型安全:完整的 TypeScript 类型定义
  • 支持 Swagger 2.0:通过 swagger2openapi 转换后支持

限制说明

  • 不支持按 tags 分割@hey-api/openapi-ts 不支持按 OpenAPI tags 自动分割生成多个文件,所有类型定义会生成在单个文件中(types.gen.ts
  • 如需按 tags 分割:可以考虑使用 openapi-generator-cli,它支持按 schema 分割生成多个文件(models/ 目录)

安装

建议在docker容器中运行,避免本地环境依赖复杂

npm install -D @hey-api/openapi-ts

使用方式

后端 Makefile 配置

FRONT_TYPES_DIR ?= ./frontend-types

generate-front-types: swagger ## 生成前端TypeScript类型定义(多文件)
@echo "generating frontend types to $(FRONT_TYPES_DIR)..."
@mkdir -p $(FRONT_TYPES_DIR)
docker run --rm \
-v $(PWD)/docs/swagger.yaml:/swagger.yaml:ro \
-v $(PWD)/$(FRONT_TYPES_DIR):/output \
node:18-alpine sh -c "\
npm install -g swagger2openapi @hey-api/openapi-ts && \
swagger2openapi /swagger.yaml -o /output/openapi3.yaml && \
openapi-ts --input /output/openapi3.yaml --output /output --types --schemas"
@echo "Frontend types generated to $(FRONT_TYPES_DIR)"

配置文件方式

创建 openapi-ts.config.ts

export default {
input: 'docs/swagger.yaml',
output: {
path: 'src/api/types',
},
client: {
// 不生成服务代码,只生成类型
enabled: false,
},
types: {
// 生成类型定义
enabled: true,
},
schemas: {
// 生成 schema 类型
enabled: true,
},
};

生成结果

生成的文件结构:

frontend-types/
├── types.gen.ts # 所有类型定义(单个文件)
├── schemas/ # Schema 类型(如果启用)
├── client/ # 客户端代码(如果启用)
│ ├── client.gen.ts
│ └── ...
├── core/ # 核心工具代码(如果启用)
│ └── ...
├── sdk.gen.ts # SDK 方法(如果启用)
└── index.ts # 统一导出

注意:如果只生成类型(client: { enabled: false }),主要文件是 types.gen.ts,包含所有类型定义。

生成文件示例

types.gen.ts(类型定义文件):

export interface UserInfo {
uid: number;
username: string;
email: string;
role: string;
}

export interface LoginRequest {
username: string;
password: string;
login_type: string;
}

export interface LoginResponse {
code: number;
msg: string;
data: {
token: string;
uid: number;
expire_at: number;
};
}

sdk.gen.ts(如果启用客户端代码生成):

import type { UserInfo, LoginRequest, LoginResponse } from './types.gen';

export const getApiV1UserInfo = (options: Options<GetApiV1UserInfoData>) => {
return client.get<GetApiV1UserInfoResponses>({
url: '/api/v1/user/info',
...options
});
};

export const postApiV1Login = (options: Options<PostApiV1LoginData>) => {
return client.post<PostApiV1LoginResponses>({
url: '/api/v1/login',
...options
});
};

前端集成

复制生成的代码

将后端项目的 frontend-types/ 目录复制到前端项目的 src/api/types/ 目录。

使用生成的类型

import type { UserInfo, LoginRequest, LoginResponse } from '@/api/types/types.gen';

// 自行封装 API 调用
export async function getUserInfo(uid: number): Promise<UserInfo> {
const response = await axios.get(`/api/v1/user/info?uid=${uid}`);
return response.data.data;
}

export async function login(request: LoginRequest): Promise<LoginResponse> {
const response = await axios.post('/api/v1/login', request);
return response.data;
}

使用生成的 SDK(如果启用客户端代码)

import { getApiV1UserInfo, postApiV1Login } from '@/api/types/sdk.gen';
import type { GetApiV1UserInfoData, PostApiV1LoginData } from '@/api/types/types.gen';

// 使用生成的 SDK 方法
const userInfo = await getApiV1UserInfo({ params: { uid: 123 } });
const loginResult = await postApiV1Login({ body: { username: 'admin', password: '123456' } });

配置选项

命令行参数

openapi-ts --input swagger.yaml --output ./output \
--types \ # 生成类型定义
--schemas \ # 生成 schema 类型
--client \ # 生成客户端代码
--services # 生成服务类

配置文件选项

export default {
input: 'docs/swagger.yaml',
output: {
path: 'src/api/types',
},
client: {
enabled: true, // 是否生成客户端代码
name: 'ApiClient', // 客户端类名
},
services: {
enabled: true, // 是否生成服务类
},
types: {
enabled: true, // 是否生成类型定义
},
schemas: {
enabled: true, // 是否生成 schema 类型
},
};

最佳实践

只生成类型定义

如果只需要类型定义,配置 client: { enabled: false }

export default defineConfig({
input: './docs/swagger.yaml',
output: {
path: './account-types',
},
client: {
enabled: false, // 不生成客户端代码
},
types: {
enabled: true, // 只生成类型定义
},
});

生成后只保留 types.gen.ts 文件,删除其他文件。

类型导入规范

// ✅ 推荐:从 types.gen.ts 导入需要的类型
import type { UserInfo, LoginRequest, LoginResponse } from '@/api/types/types.gen';

// ❌ 不推荐:导入所有类型
import type * as Types from '@/api/types/types.gen';

与现有代码集成

如果项目已有手动封装的 API,可以保持原有接口不变,内部使用生成的类型:

// 使用生成的类型
import type { UserInfo } from '@/api/types/types.gen';

// 保持原有的 API 封装
export async function getUserInfo(uid: number): Promise<UserInfo> {
const response = await axios.get(`/api/v1/user/info?uid=${uid}`);
return response.data.data;
}

优势

  • 灵活配置:可以只生成类型,也可以生成完整服务
  • 类型安全:完整的 TypeScript 类型支持
  • 活跃维护:持续更新,修复问题及时
  • 轻量级:只生成类型时,输出文件简洁

常见问题

Swagger 2.0 支持

@hey-api/openapi-ts 需要 OpenAPI 3.x,如果后端使用 swaggo/swag 生成 Swagger 2.0,需要先使用 swagger2openapi 转换。

文件分割限制

重要@hey-api/openapi-ts 不支持按 OpenAPI tags 分割生成多个文件。所有类型定义会生成在单个 types.gen.ts 文件中。

如果需要按模块分割类型文件,可以考虑:

  1. 使用 openapi-generator-cli,它支持按 schema 分割(models/ 目录)
  2. 使用自定义脚本后处理,按 tags 分割 types.gen.ts 文件

与现有代码集成

如果项目已有手动封装的 API,可以保持原有接口不变,内部使用生成的类型,实现渐进式迁移。

参考