Type Generation - Quick Reference
When NOT to Use This Skill
-
Manual type writing - Use TypeScript skills
-
Contract validation - Use openapi-contract skill
-
GraphQL queries - Use graphql-contract skill
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: typescript for advanced type patterns.
Tool Comparison
Tool Source Output Use Case
openapi-typescript OpenAPI 3.x TypeScript types REST APIs
openapi-fetch OpenAPI 3.x Type-safe client REST + fetch
graphql-codegen GraphQL schema Types + hooks GraphQL APIs
orval OpenAPI 3.x Types + client REST + Axios/fetch
swagger-typescript-api OpenAPI/Swagger Types + class client REST APIs
openapi-typescript Setup
Installation
npm install -D openapi-typescript
Basic Usage
From local file
npx openapi-typescript openapi.yaml -o src/api/types.ts
From URL
npx openapi-typescript https://api.example.com/openapi.json -o src/api/types.ts
Watch mode
npx openapi-typescript openapi.yaml -o src/api/types.ts --watch
Configuration (openapi-ts.config.ts)
import { defineConfig } from 'openapi-typescript';
export default defineConfig({ input: './openapi.yaml', output: './src/api/types.ts', // Alphabetize output alphabetize: true, // Export type for each path exportType: true, // Transform property names transform: (schemaObject) => { // Custom transformation return schemaObject; }, });
Generated Types Usage
// Generated types import type { paths, components } from './api/types';
// Request body type type CreateUserBody = paths['/users']['post']['requestBody']['content']['application/json'];
// Response type type UserResponse = paths['/users']['get']['responses']['200']['content']['application/json'];
// Schema type type User = components['schemas']['User'];
// Path parameters type UserParams = paths['/users/{id}']['parameters']['path'];
openapi-fetch Setup (Type-safe Client)
Installation
npm install openapi-fetch npm install -D openapi-typescript
Generate Types + Create Client
Generate types first
npx openapi-typescript openapi.yaml -o src/api/types.ts
// src/api/client.ts import createClient from 'openapi-fetch'; import type { paths } from './types';
const client = createClient<paths>({ baseUrl: 'https://api.example.com', headers: { 'Content-Type': 'application/json', }, });
export default client;
Usage with Type Safety
import client from './api/client';
// GET request - fully typed const { data, error } = await client.GET('/users/{id}', { params: { path: { id: '123' }, query: { include: 'profile' }, }, });
if (data) { // data is properly typed as User console.log(data.name); }
// POST request const { data: newUser } = await client.POST('/users', { body: { name: 'John', email: 'john@example.com', }, });
// With React Query import { useQuery, useMutation } from '@tanstack/react-query';
function useUser(id: string) { return useQuery({ queryKey: ['user', id], queryFn: async () => { const { data, error } = await client.GET('/users/{id}', { params: { path: { id } }, }); if (error) throw error; return data; }, }); }
GraphQL Codegen Setup
Installation
npm install -D @graphql-codegen/cli @graphql-codegen/typescript
@graphql-codegen/typescript-operations @graphql-codegen/typescript-react-query
Configuration (codegen.ts)
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = { schema: 'https://api.example.com/graphql', documents: ['src//*.graphql', 'src//*.tsx'], generates: { './src/generated/graphql.ts': { plugins: [ 'typescript', 'typescript-operations', 'typescript-react-query', ], config: { fetcher: { endpoint: 'https://api.example.com/graphql', }, exposeQueryKeys: true, exposeFetcher: true, }, }, }, };
export default config;
GraphQL Document
src/graphql/users.graphql
query GetUser($id: ID!) { user(id: $id) { id name email createdAt } }
mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name email } }
Generate and Use
npx graphql-codegen
// Auto-generated hooks import { useGetUserQuery, useCreateUserMutation } from './generated/graphql';
function UserProfile({ userId }: { userId: string }) { const { data, isLoading } = useGetUserQuery({ id: userId });
if (isLoading) return <Spinner />; return <div>{data?.user?.name}</div>; }
function CreateUserForm() { const mutation = useCreateUserMutation();
const onSubmit = (data: CreateUserInput) => { mutation.mutate({ input: data }); };
return <form onSubmit={handleSubmit(onSubmit)}>...</form>; }
Orval Setup (OpenAPI + Client Generation)
Installation
npm install -D orval
Configuration (orval.config.ts)
import { defineConfig } from 'orval';
export default defineConfig({ petstore: { input: './openapi.yaml', output: { target: './src/api/endpoints.ts', schemas: './src/api/model', client: 'react-query', mode: 'tags-split', mock: true, }, }, });
Generate
npx orval
Generated Output
// Auto-generated hooks with React Query import { useGetUsers, useCreateUser } from './api/endpoints';
function UserList() { const { data: users } = useGetUsers(); return <ul>{users?.map(u => <li key={u.id}>{u.name}</li>)}</ul>; }
Package.json Scripts
{ "scripts": { "generate:types": "openapi-typescript openapi.yaml -o src/api/types.ts", "generate:client": "orval", "generate:graphql": "graphql-codegen", "generate": "npm run generate:types && npm run generate:client", "predev": "npm run generate", "prebuild": "npm run generate" } }
CI/CD Integration
GitHub Actions
name: Type Generation on: [push, pull_request]
jobs: generate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Generate types
run: npm run generate:types
- name: Check for changes
run: |
git diff --exit-code src/api/types.ts || \
(echo "Types are out of date! Run 'npm run generate:types'" && exit 1)
Best Practices
- Version Control Generated Files
Option A: Don't commit (regenerate in CI)
src/api/types.ts src/generated/
Option B: Commit (recommended for visibility)
Don't add to .gitignore, commit generated files
- Pre-commit Hook
// package.json { "lint-staged": { "openapi.yaml": [ "npm run generate:types", "git add src/api/types.ts" ] } }
- Watch Mode in Development
{ "scripts": { "dev": "concurrently "npm run generate:types -- --watch" "vite"" } }
- Type Re-exports
// src/api/index.ts export type { User, CreateUserRequest, UpdateUserRequest, UserListResponse, } from './types';
export { default as client } from './client';
Common Patterns
Nullable Fields
OpenAPI
components: schemas: User: properties: middleName: type: string nullable: true
// Generated interface User { middleName: string | null; }
// Usage const displayName = user.middleName ?? 'N/A';
Discriminated Unions
OpenAPI
components: schemas: Shape: oneOf: - $ref: '#/components/schemas/Circle' - $ref: '#/components/schemas/Rectangle' discriminator: propertyName: type
// Generated type Shape = Circle | Rectangle;
// Usage with type narrowing function getArea(shape: Shape): number { switch (shape.type) { case 'circle': return Math.PI * shape.radius ** 2; case 'rectangle': return shape.width * shape.height; } }
Enums
OpenAPI
components: schemas: UserRole: type: string enum: [admin, user, guest]
// Generated as union type type UserRole = 'admin' | 'user' | 'guest';
// Or as const object const UserRole = { Admin: 'admin', User: 'user', Guest: 'guest', } as const;
Anti-Patterns
Anti-Pattern Why It's Bad Correct Approach
Manual type sync Drift, errors Auto-generate from spec
any for API responses No type safety Use generated types
Ignoring nullable Runtime errors Handle null properly
No pre-commit generation Outdated types Hook into git workflow
Editing generated files Lost on regenerate Extend types separately
Quick Troubleshooting
Issue Likely Cause Solution
Types don't match API Outdated spec Update and regenerate
Generation fails Invalid OpenAPI Validate spec first
Missing properties Optional vs required Check OpenAPI schema
Wrong enum values Spec changed Regenerate types
Import errors Path configuration Check tsconfig paths
Related Skills
-
OpenAPI Contract
-
GraphQL Contract
-
DTO Sync Patterns