type-generation

v2026.09.24

Automatic type generation from OpenAPI and GraphQL schemas. Covers openapi-typescript, graphql-codegen, and contract sync. USE WHEN: user asks about "openapi-typescript", "graphql-codegen", "generate types from API", "type generation", "API types", "schema to TypeScript" DO NOT USE FOR: manual type definitions - use TypeScript skills, contract validation - use `openapi-contract` skill

GitHub
安装命令
npx skhub add claude-dev-suite/type-generation
Markdown
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

ToolSourceOutputUse Case
openapi-typescriptOpenAPI 3.xTypeScript typesREST APIs
openapi-fetchOpenAPI 3.xType-safe clientREST + fetch
graphql-codegenGraphQL schemaTypes + hooksGraphQL APIs
orvalOpenAPI 3.xTypes + clientREST + Axios/fetch
swagger-typescript-apiOpenAPI/SwaggerTypes + class clientREST 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-PatternWhy It's BadCorrect Approach
Manual type syncDrift, errorsAuto-generate from spec
any for API responsesNo type safetyUse generated types
Ignoring nullableRuntime errorsHandle null properly
No pre-commit generationOutdated typesHook into git workflow
Editing generated filesLost on regenerateExtend types separately

Quick Troubleshooting

IssueLikely CauseSolution
Types don't match APIOutdated specUpdate and regenerate
Generation failsInvalid OpenAPIValidate spec first
Missing propertiesOptional vs requiredCheck OpenAPI schema
Wrong enum valuesSpec changedRegenerate types
Import errorsPath configurationCheck tsconfig paths

Related Skills

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

Sep 24, 2026

分类

未分类

许可证

MIT

源路径

skills/integration-validation/type-generation

默认分支

main

最新提交

9496306

Tree SHA

fe4e2f1