type-generation

SKILL.md

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

1. 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

2. Pre-commit Hook

// package.json
{
  "lint-staged": {
    "openapi.yaml": [
      "npm run generate:types",
      "git add src/api/types.ts"
    ]
  }
}

3. Watch Mode in Development

{
  "scripts": {
    "dev": "concurrently \"npm run generate:types -- --watch\" \"vite\""
  }
}

4. 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

Weekly Installs
11
GitHub Stars
2
First Seen
9 days ago
Installed on
cursor10
gemini-cli10
amp10
cline10
github-copilot10
codex10