openapi-to-typescript

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OpenAPI to TypeScript

OpenAPI 转 TypeScript

Converts OpenAPI 3.0 specifications to TypeScript interfaces and type guards.
Input: OpenAPI file (JSON or YAML) Output: TypeScript file with interfaces and type guards
将OpenAPI 3.0规范转换为TypeScript接口和类型守卫。
输入: OpenAPI文件(JSON或YAML) 输出: 包含接口和类型守卫的TypeScript文件

When to Use

使用场景

  • "generate types from openapi"
  • "convert openapi to typescript"
  • "create API interfaces"
  • "generate types from spec"
  • "从OpenAPI生成类型"
  • "将OpenAPI转换为TypeScript"
  • "创建API接口"
  • "从规范生成类型"

Workflow

工作流程

  1. Request the OpenAPI file path (if not provided)
  2. Read and validate the file (must be OpenAPI 3.0.x)
  3. Extract schemas from
    components/schemas
  4. Extract endpoints from
    paths
    (request/response types)
  5. Generate TypeScript (interfaces + type guards)
  6. Ask where to save (default:
    types/api.ts
    in current directory)
  7. Write the file
  1. 请求OpenAPI文件路径(如果未提供)
  2. 读取并验证文件(必须是OpenAPI 3.0.x版本)
  3. components/schemas
    中提取Schema
  4. paths
    中提取端点(请求/响应类型)
  5. 生成TypeScript代码(接口 + 类型守卫)
  6. 询问保存路径(默认:当前目录下的
    types/api.ts
  7. 写入文件

OpenAPI Validation

OpenAPI 验证

Check before processing:
- Field "openapi" must exist and start with "3.0"
- Field "paths" must exist
- Field "components.schemas" must exist (if there are types)
If invalid, report the error and stop.
处理前检查:
- Field "openapi" must exist and start with "3.0"
- Field "paths" must exist
- Field "components.schemas" must exist (if there are types)
如果验证不通过,报告错误并终止处理。

Type Mapping

类型映射

Primitives

基本类型

OpenAPITypeScript
string
string
number
number
integer
number
boolean
boolean
null
null
OpenAPITypeScript
string
string
number
number
integer
number
boolean
boolean
null
null

Format Modifiers

格式修饰符

FormatTypeScript
uuid
string
(comment UUID)
date
string
(comment date)
date-time
string
(comment ISO)
email
string
(comment email)
uri
string
(comment URI)
FormatTypeScript
uuid
string
(comment UUID)
date
string
(comment date)
date-time
string
(comment ISO)
email
string
(comment email)
uri
string
(comment URI)

Complex Types

复杂类型

Object:
typescript
// OpenAPI: type: object, properties: {id, name}, required: [id]
interface Example {
  id: string;      // required: no ?
  name?: string;   // optional: with ?
}
Array:
typescript
// OpenAPI: type: array, items: {type: string}
type Names = string[];
Enum:
typescript
// OpenAPI: type: string, enum: [active, draft]
type Status = "active" | "draft";
oneOf (Union):
typescript
// OpenAPI: oneOf: [{$ref: Cat}, {$ref: Dog}]
type Pet = Cat | Dog;
allOf (Intersection/Extends):
typescript
// OpenAPI: allOf: [{$ref: Base}, {type: object, properties: ...}]
interface Extended extends Base {
  extraField: string;
}
对象类型:
typescript
// OpenAPI: type: object, properties: {id, name}, required: [id]
interface Example {
  id: string;      // required: no ?
  name?: string;   // optional: with ?
}
数组类型:
typescript
// OpenAPI: type: array, items: {type: string}
type Names = string[];
枚举类型:
typescript
// OpenAPI: type: string, enum: [active, draft]
type Status = "active" | "draft";
oneOf(联合类型):
typescript
// OpenAPI: oneOf: [{$ref: Cat}, {$ref: Dog}]
type Pet = Cat | Dog;
allOf(交叉/继承类型):
typescript
// OpenAPI: allOf: [{$ref: Base}, {type: object, properties: ...}]
interface Extended extends Base {
  extraField: string;
}

Code Generation

代码生成

File Header

文件头部

typescript
/**
 * Auto-generated from: {source_file}
 * Generated at: {timestamp}
 *
 * DO NOT EDIT MANUALLY - Regenerate from OpenAPI schema
 */
typescript
/**
 * Auto-generated from: {source_file}
 * Generated at: {timestamp}
 *
 * DO NOT EDIT MANUALLY - Regenerate from OpenAPI schema
 */

Interfaces (from components/schemas)

接口(来自components/schemas)

For each schema in
components/schemas
:
typescript
export interface Product {
  /** Product unique identifier */
  id: string;

  /** Product title */
  title: string;

  /** Product price */
  price: number;

  /** Created timestamp */
  created_at?: string;
}
  • Use OpenAPI description as JSDoc
  • Fields in
    required[]
    have no
    ?
  • Fields outside
    required[]
    have
    ?
对于
components/schemas
中的每个Schema:
typescript
export interface Product {
  /** Product unique identifier */
  id: string;

  /** Product title */
  title: string;

  /** Product price */
  price: number;

  /** Created timestamp */
  created_at?: string;
}
  • 使用OpenAPI中的description作为JSDoc注释
  • required[]
    中的字段不带
    ?
  • required[]
    外的字段带
    ?

Request/Response Types (from paths)

请求/响应类型(来自paths)

For each endpoint in
paths
:
typescript
// GET /products - query params
export interface GetProductsRequest {
  page?: number;
  limit?: number;
}

// GET /products - response 200
export type GetProductsResponse = ProductList;

// POST /products - request body
export interface CreateProductRequest {
  title: string;
  price: number;
}

// POST /products - response 201
export type CreateProductResponse = Product;
Naming convention:
  • {Method}{Path}Request
    for params/body
  • {Method}{Path}Response
    for response
对于
paths
中的每个端点:
typescript
// GET /products - query params
export interface GetProductsRequest {
  page?: number;
  limit?: number;
}

// GET /products - response 200
export type GetProductsResponse = ProductList;

// POST /products - request body
export interface CreateProductRequest {
  title: string;
  price: number;
}

// POST /products - response 201
export type CreateProductResponse = Product;
命名规则:
  • 请求参数/请求体使用
    {Method}{Path}Request
    格式
  • 响应使用
    {Method}{Path}Response
    格式

Type Guards

类型守卫

For each main interface, generate a type guard:
typescript
export function isProduct(value: unknown): value is Product {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    typeof (value as any).id === 'string' &&
    'title' in value &&
    typeof (value as any).title === 'string' &&
    'price' in value &&
    typeof (value as any).price === 'number'
  );
}
Type guard rules:
  • Check
    typeof value === 'object' && value !== null
  • For each required field: check
    'field' in value
  • For primitive fields: check
    typeof
  • For arrays: check
    Array.isArray()
  • For enums: check
    .includes()
为每个主接口生成类型守卫:
typescript
export function isProduct(value: unknown): value is Product {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    typeof (value as any).id === 'string' &&
    'title' in value &&
    typeof (value as any).title === 'string' &&
    'price' in value &&
    typeof (value as any).price === 'number'
  );
}
类型守卫规则:
  • 检查
    typeof value === 'object' && value !== null
  • 对于每个必填字段:检查
    'field' in value
  • 对于基本类型字段:检查
    typeof
  • 对于数组:检查
    Array.isArray()
  • 对于枚举:检查
    .includes()

Error Type (always include)

错误类型(始终包含)

typescript
export interface ApiError {
  status: number;
  error: string;
  detail?: string;
}

export function isApiError(value: unknown): value is ApiError {
  return (
    typeof value === 'object' &&
    value !== null &&
    'status' in value &&
    typeof (value as any).status === 'number' &&
    'error' in value &&
    typeof (value as any).error === 'string'
  );
}
typescript
export interface ApiError {
  status: number;
  error: string;
  detail?: string;
}

export function isApiError(value: unknown): value is ApiError {
  return (
    typeof value === 'object' &&
    value !== null &&
    'status' in value &&
    typeof (value as any).status === 'number' &&
    'error' in value &&
    typeof (value as any).error === 'string'
  );
}

$ref Resolution

$ref 解析

When encountering
{"$ref": "#/components/schemas/Product"}
:
  1. Extract the schema name (
    Product
    )
  2. Use the type directly (don't resolve inline)
typescript
// OpenAPI: items: {$ref: "#/components/schemas/Product"}
// TypeScript:
items: Product[]  // reference, not inline
当遇到
{"$ref": "#/components/schemas/Product"}
时:
  1. 提取Schema名称(
    Product
  2. 直接使用该类型(不内联解析)
typescript
// OpenAPI: items: {$ref: "#/components/schemas/Product"}
// TypeScript:
items: Product[]  // reference, not inline

Complete Example

完整示例

Input (OpenAPI):
json
{
  "openapi": "3.0.0",
  "components": {
    "schemas": {
      "User": {
        "type": "object",
        "properties": {
          "id": {"type": "string", "format": "uuid"},
          "email": {"type": "string", "format": "email"},
          "role": {"type": "string", "enum": ["admin", "user"]}
        },
        "required": ["id", "email", "role"]
      }
    }
  },
  "paths": {
    "/users/{id}": {
      "get": {
        "parameters": [{"name": "id", "in": "path", "required": true}],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/User"}
              }
            }
          }
        }
      }
    }
  }
}
Output (TypeScript):
typescript
/**
 * Auto-generated from: api.openapi.json
 * Generated at: 2025-01-15T10:30:00Z
 *
 * DO NOT EDIT MANUALLY - Regenerate from OpenAPI schema
 */

// ============================================================================
// Types
// ============================================================================

export type UserRole = "admin" | "user";

export interface User {
  /** UUID */
  id: string;

  /** Email */
  email: string;

  role: UserRole;
}

// ============================================================================
// Request/Response Types
// ============================================================================

export interface GetUserByIdRequest {
  id: string;
}

export type GetUserByIdResponse = User;

// ============================================================================
// Type Guards
// ============================================================================

export function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    typeof (value as any).id === 'string' &&
    'email' in value &&
    typeof (value as any).email === 'string' &&
    'role' in value &&
    ['admin', 'user'].includes((value as any).role)
  );
}

// ============================================================================
// Error Types
// ============================================================================

export interface ApiError {
  status: number;
  error: string;
  detail?: string;
}

export function isApiError(value: unknown): value is ApiError {
  return (
    typeof value === 'object' &&
    value !== null &&
    'status' in value &&
    typeof (value as any).status === 'number' &&
    'error' in value &&
    typeof (value as any).error === 'string'
  );
}
输入(OpenAPI):
json
{
  "openapi": "3.0.0",
  "components": {
    "schemas": {
      "User": {
        "type": "object",
        "properties": {
          "id": {"type": "string", "format": "uuid"},
          "email": {"type": "string", "format": "email"},
          "role": {"type": "string", "enum": ["admin", "user"]}
        },
        "required": ["id", "email", "role"]
      }
    }
  },
  "paths": {
    "/users/{id}": {
      "get": {
        "parameters": [{"name": "id", "in": "path", "required": true}],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/User"}
              }
            }
          }
        }
      }
    }
  }
}
输出(TypeScript):
typescript
/**
 * Auto-generated from: api.openapi.json
 * Generated at: 2025-01-15T10:30:00Z
 *
 * DO NOT EDIT MANUALLY - Regenerate from OpenAPI schema
 */

// ============================================================================
// Types
// ============================================================================

export type UserRole = "admin" | "user";

export interface User {
  /** UUID */
  id: string;

  /** Email */
  email: string;

  role: UserRole;
}

// ============================================================================
// Request/Response Types
// ============================================================================

export interface GetUserByIdRequest {
  id: string;
}

export type GetUserByIdResponse = User;

// ============================================================================
// Type Guards
// ============================================================================

export function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    typeof (value as any).id === 'string' &&
    'email' in value &&
    typeof (value as any).email === 'string' &&
    'role' in value &&
    ['admin', 'user'].includes((value as any).role)
  );
}

// ============================================================================
// Error Types
// ============================================================================

export interface ApiError {
  status: number;
  error: string;
  detail?: string;
}

export function isApiError(value: unknown): value is ApiError {
  return (
    typeof value === 'object' &&
    value !== null &&
    'status' in value &&
    typeof (value as any).status === 'number' &&
    'error' in value &&
    typeof (value as any).error === 'string'
  );
}

Common Errors

常见错误

ErrorAction
OpenAPI version != 3.0.xReport that only 3.0 is supported
$ref not foundList missing refs
Unknown typeUse
unknown
and warn
Circular referenceUse type alias with lazy reference
错误处理方式
OpenAPI版本 != 3.0.x提示仅支持3.0版本
$ref未找到列出缺失的引用
未知类型使用
unknown
并发出警告
循环引用使用带延迟引用的类型别名