SKILL.md
唯讀
名稱
openapi-to-typescript
描述
將 OpenAPI 3.0 JSON/YAML 轉換為 TypeScript 介面與型別守衛。當使用者要求從 OpenAPI 產生型別、將 schema 轉換為 TS、建立 API 介面,或從 API 規格產生 TypeScript 型別時,應使用此技能。
OpenAPI 轉 TypeScript
將 OpenAPI 3.0 規格轉換為 TypeScript 介面與型別守衛。
輸入: OpenAPI 檔案(JSON 或 YAML)
輸出: 包含介面與型別守衛的 TypeScript 檔案
使用時機
- "從 openapi 產生型別"
- "將 openapi 轉換為 typescript"
- "建立 API 介面"
- "從規格產生型別"
工作流程
- 要求提供 OpenAPI 檔案路徑(若未提供)
- 讀取並驗證檔案(必須是 OpenAPI 3.0.x)
- 從
components/schemas提取 schemas - 從
paths提取端點(請求/回應型別) - 產生 TypeScript(介面 + 型別守衛)
- 詢問儲存位置(預設:目前目錄下的
types/api.ts) - 寫入檔案
OpenAPI 驗證
處理前檢查:
- 必須存在 "openapi" 欄位,且開頭為 "3.0"
- 必須存在 "paths" 欄位
- 若存在型別,則必須存在 "components.schemas" 欄位
若無效,回報錯誤並停止。
型別對應
基本型別
| OpenAPI | TypeScript |
|---|---|
string |
string |
number |
number |
integer |
number |
boolean |
boolean |
null |
null |
格式修飾
| 格式 | TypeScript |
|---|---|
uuid |
string(註解 UUID) |
date |
string(註解 date) |
date-time |
string(註解 ISO) |
email |
string(註解 email) |
uri |
string(註解 URI) |
複合型別
物件:
// OpenAPI: type: object, properties: {id, name}, required: [id]
interface Example {
id: string; // 必要:無 ?
name?: string; // 選填:有 ?
}
陣列:
// OpenAPI: type: array, items: {type: string}
type Names = string[];
列舉:
// OpenAPI: type: string, enum: [active, draft]
type Status = "active" | "draft";
oneOf(聯集):
// OpenAPI: oneOf: [{$ref: Cat}, {$ref: Dog}]
type Pet = Cat | Dog;
allOf(交集/繼承):
// OpenAPI: allOf: [{$ref: Base}, {type: object, properties: ...}]
interface Extended extends Base {
extraField: string;
}
程式碼產生
檔案標頭
/**
* 自動產生自:{source_file}
* 產生時間:{timestamp}
*
* 請勿手動編輯 - 請從 OpenAPI schema 重新產生
*/
介面(來自 components/schemas)
針對 components/schemas 中的每個 schema:
export interface Product {
/** 產品唯一識別碼 */
id: string;
/** 產品標題 */
title: string;
/** 產品價格 */
price: number;
/** 建立時間戳記 */
created_at?: string;
}
- 使用 OpenAPI 的 description 作為 JSDoc
- 在
required[]中的欄位不加? - 不在
required[]中的欄位加?
請求/回應型別(來自 paths)
針對 paths 中的每個端點:
// GET /products - 查詢參數
export interface GetProductsRequest {
page?: number;
limit?: number;
}
// GET /products - 回應 200
export type GetProductsResponse = ProductList;
// POST /products - 請求主體
export interface CreateProductRequest {
title: string;
price: number;
}
// POST /products - 回應 201
export type CreateProductResponse = Product;
命名慣例:
{Method}{Path}Request用於參數/主體{Method}{Path}Response用於回應
型別守衛
針對每個主要介面,產生型別守衛:
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()
錯誤型別(一律包含)
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 解析
遇到 {"$ref": "#/components/schemas/Product"} 時:
- 提取 schema 名稱(
Product) - 直接使用該型別(不內聯解析)
// OpenAPI: items: {$ref: "#/components/schemas/Product"}
// TypeScript:
items: Product[] // 引用,非內聯
完整範例
輸入(OpenAPI):
{
"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):
/**
* 自動產生自:api.openapi.json
* 產生時間:2025-01-15T10:30:00Z
*
* 請勿手動編輯 - 請從 OpenAPI schema 重新產生
*/
// ============================================================================
// 型別
// ============================================================================
export type UserRole = "admin" | "user";
export interface User {
/** UUID */
id: string;
/** Email */
email: string;
role: UserRole;
}
// ============================================================================
// 請求/回應型別
// ============================================================================
export interface GetUserByIdRequest {
id: string;
}
export type GetUserByIdResponse = User;
// ============================================================================
// 型別守衛
// ============================================================================
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)
);
}
// ============================================================================
// 錯誤型別
// ============================================================================
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 版本不是 3.0.x | 回報僅支援 3.0 |
| $ref 找不到 | 列出遺失的引用 |
| 未知型別 | 使用 unknown 並發出警告 |
| 循環引用 | 使用型別別名搭配延遲引用 |






