generating-typescript-types-from-apis

Generates TypeScript interfaces from API responses or OpenAPI schemas. Use when the user asks about typing API responses, creating interfaces from JSON, parsing Swagger/OpenAPI, or keeping types in sync with backend.

16 stars

Best use case

generating-typescript-types-from-apis is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generates TypeScript interfaces from API responses or OpenAPI schemas. Use when the user asks about typing API responses, creating interfaces from JSON, parsing Swagger/OpenAPI, or keeping types in sync with backend.

Teams using generating-typescript-types-from-apis should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/generating-typescript-types-from-apis/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/backend/generating-typescript-types-from-apis/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/generating-typescript-types-from-apis/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How generating-typescript-types-from-apis Compares

Feature / Agentgenerating-typescript-types-from-apisStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generates TypeScript interfaces from API responses or OpenAPI schemas. Use when the user asks about typing API responses, creating interfaces from JSON, parsing Swagger/OpenAPI, or keeping types in sync with backend.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# API Response → TypeScript Types

## When to use this skill

- User asks to type an API response
- User has JSON and needs TypeScript interfaces
- User mentions OpenAPI or Swagger schemas
- User wants to generate types from endpoints
- User asks about keeping frontend/backend types in sync

## Workflow

- [ ] Identify API source (JSON response, OpenAPI, endpoint)
- [ ] Parse response structure
- [ ] Generate TypeScript interfaces
- [ ] Handle nested objects and arrays
- [ ] Add JSDoc comments
- [ ] Export types to appropriate location

## Instructions

### Step 1: Identify Source Type

| Source          | Approach              |
| --------------- | --------------------- |
| JSON response   | Parse and infer types |
| OpenAPI/Swagger | Use generator tool    |
| GraphQL         | Use codegen           |
| Live endpoint   | Fetch and parse       |

### Step 2: Parse JSON Response

**Sample API response:**

```json
{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "isActive": true,
  "roles": ["admin", "user"],
  "profile": {
    "avatar": "https://example.com/avatar.jpg",
    "bio": null,
    "socialLinks": [
      { "platform": "twitter", "url": "https://twitter.com/john" }
    ]
  },
  "createdAt": "2026-01-18T10:00:00Z",
  "metadata": {}
}
```

**Generated TypeScript:**

```typescript
// types/api/user.ts

export interface User {
  /** Unique identifier */
  id: number;
  /** User's full name */
  name: string;
  /** Email address */
  email: string;
  /** Whether the user account is active */
  isActive: boolean;
  /** Assigned roles */
  roles: string[];
  /** User profile information */
  profile: UserProfile;
  /** Account creation timestamp (ISO 8601) */
  createdAt: string;
  /** Additional metadata */
  metadata: Record<string, unknown>;
}

export interface UserProfile {
  /** Avatar image URL */
  avatar: string;
  /** User biography */
  bio: string | null;
  /** Social media links */
  socialLinks: SocialLink[];
}

export interface SocialLink {
  /** Platform name */
  platform: string;
  /** Profile URL */
  url: string;
}
```

### Step 3: Type Inference Rules

| JSON Value      | TypeScript Type             |
| --------------- | --------------------------- |
| `123`           | `number`                    |
| `"text"`        | `string`                    |
| `true`/`false`  | `boolean`                   |
| `null`          | `null` (or `T \| null`)     |
| `[]`            | `T[]` (infer from items)    |
| `{}` empty      | `Record<string, unknown>`   |
| `{}` with keys  | Named interface             |
| ISO date string | `string` (add comment)      |
| UUID string     | `string` (add branded type) |

**Branded types for special strings:**

```typescript
// types/branded.ts
export type UUID = string & { readonly __brand: "UUID" };
export type ISODateString = string & { readonly __brand: "ISODateString" };
export type Email = string & { readonly __brand: "Email" };

// Usage
export interface User {
  id: UUID;
  email: Email;
  createdAt: ISODateString;
}
```

### Step 4: Handle Arrays

**Homogeneous array:**

```typescript
// JSON: [1, 2, 3]
items: number[];

// JSON: ["a", "b"]
tags: string[];
```

**Array of objects:**

```typescript
// JSON: [{ "id": 1, "name": "Item" }]
items: Item[];

interface Item {
  id: number;
  name: string;
}
```

**Mixed array (avoid if possible):**

```typescript
// JSON: [1, "two", true]
values: (number | string | boolean)[];
```

**Tuple (fixed length, known types):**

```typescript
// JSON: [37.7749, -122.4194] (lat/lng)
coordinates: [number, number];
```

### Step 5: Handle Optional Fields

**Detect optional fields from multiple samples:**

```typescript
// Sample 1: { "name": "John", "nickname": "Johnny" }
// Sample 2: { "name": "Jane" }

export interface User {
  name: string;
  nickname?: string; // Optional - not present in all responses
}
```

**Nullable vs optional:**

```typescript
export interface User {
  bio: string | null; // Present but can be null
  nickname?: string; // May not be present
  avatar?: string | null; // May not be present, or null
}
```

### Step 6: API Response Wrappers

**Paginated response:**

```typescript
export interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    perPage: number;
    total: number;
    totalPages: number;
  };
}

// Usage
type UsersResponse = PaginatedResponse<User>;
```

**API envelope:**

```typescript
export interface ApiResponse<T> {
  success: boolean;
  data: T;
  error?: ApiError;
}

export interface ApiError {
  code: string;
  message: string;
  details?: Record<string, string[]>;
}

// Usage
type UserResponse = ApiResponse<User>;
type UsersResponse = ApiResponse<User[]>;
```

### Step 7: OpenAPI/Swagger Generation

**Using openapi-typescript:**

```bash
npm install -D openapi-typescript
```

```bash
# From URL
npx openapi-typescript https://api.example.com/openapi.json -o types/api.ts

# From local file
npx openapi-typescript ./openapi.yaml -o types/api.ts

# Watch mode
npx openapi-typescript ./openapi.yaml -o types/api.ts --watch
```

**Generated usage:**

```typescript
import type { paths, components } from "./types/api";

// Extract response type
type User = components["schemas"]["User"];

// Extract endpoint types
type GetUsersResponse =
  paths["/users"]["get"]["responses"]["200"]["content"]["application/json"];
type CreateUserBody =
  paths["/users"]["post"]["requestBody"]["content"]["application/json"];
```

**With openapi-fetch for type-safe requests:**

```bash
npm install openapi-fetch
```

```typescript
import createClient from "openapi-fetch";
import type { paths } from "./types/api";

const client = createClient<paths>({ baseUrl: "https://api.example.com" });

// Fully typed request/response
const { data, error } = await client.GET("/users/{id}", {
  params: { path: { id: "123" } },
});
// data is typed as User
```

### Step 8: Fetch and Generate Script

```typescript
// scripts/generate-types.ts
import { writeFileSync } from "fs";

interface TypeDefinition {
  name: string;
  properties: PropertyDefinition[];
}

interface PropertyDefinition {
  name: string;
  type: string;
  optional: boolean;
  nullable: boolean;
  comment?: string;
}

function inferType(value: unknown, key: string): string {
  if (value === null) return "null";
  if (Array.isArray(value)) {
    if (value.length === 0) return "unknown[]";
    const itemType = inferType(value[0], `${key}Item`);
    return `${itemType}[]`;
  }
  if (typeof value === "object") {
    return toPascalCase(key);
  }
  return typeof value;
}

function toPascalCase(str: string): string {
  return str.replace(/(^|_)(\w)/g, (_, __, c) => c.toUpperCase());
}

function generateInterface(
  name: string,
  obj: Record<string, unknown>,
): string[] {
  const lines: string[] = [];
  const nested: string[] = [];

  lines.push(`export interface ${name} {`);

  for (const [key, value] of Object.entries(obj)) {
    const type = inferType(value, key);
    const nullable = value === null ? " | null" : "";

    if (typeof value === "object" && value !== null && !Array.isArray(value)) {
      nested.push(
        ...generateInterface(
          toPascalCase(key),
          value as Record<string, unknown>,
        ),
      );
    }

    lines.push(`  ${key}: ${type}${nullable};`);
  }

  lines.push("}");
  lines.push("");

  return [...nested, ...lines];
}

async function main() {
  const response = await fetch("https://api.example.com/users/1");
  const data = await response.json();

  const types = generateInterface("User", data);
  const output = types.join("\n");

  writeFileSync("types/user.ts", output);
  console.log("Generated types/user.ts");
}

main();
```

### Step 9: Keep Types in Sync

**CI check for OpenAPI changes:**

```yaml
# .github/workflows/types.yml
name: Generate API Types

on:
  schedule:
    - cron: "0 0 * * *" # Daily
  workflow_dispatch:

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate types
        run: npx openapi-typescript ${{ vars.API_SPEC_URL }} -o types/api.ts

      - name: Check for changes
        id: changes
        run: |
          if git diff --quiet types/api.ts; then
            echo "changed=false" >> $GITHUB_OUTPUT
          else
            echo "changed=true" >> $GITHUB_OUTPUT
          fi

      - name: Create PR
        if: steps.changes.outputs.changed == 'true'
        uses: peter-evans/create-pull-request@v5
        with:
          title: "chore: update API types"
          branch: update-api-types
```

**Pre-commit hook:**

```bash
# .husky/pre-commit
npx openapi-typescript ./openapi.yaml -o types/api.ts
git add types/api.ts
```

## Output Location

```
types/
├── api/
│   ├── user.ts       # User-related types
│   ├── product.ts    # Product types
│   └── index.ts      # Re-exports
├── api.ts            # OpenAPI generated (single file)
└── branded.ts        # Branded types (UUID, Email, etc.)
```

**Index file:**

```typescript
// types/api/index.ts
export * from "./user";
export * from "./product";
export type { ApiResponse, ApiError, PaginatedResponse } from "./common";
```

## Validation

Before completing:

- [ ] All interfaces have JSDoc comments
- [ ] Nested objects have named interfaces
- [ ] Optional fields marked with `?`
- [ ] Nullable fields use `| null`
- [ ] Arrays are properly typed
- [ ] No `any` types in output
- [ ] Types compile without errors

```bash
# Validate generated types
npx tsc --noEmit types/**/*.ts
```

## Error Handling

- **Empty object `{}`**: Use `Record<string, unknown>` not `object`.
- **Mixed arrays**: Union type or `unknown[]`; flag for manual review.
- **Circular references**: OpenAPI generators handle this; manual parsing needs tracking.
- **Conflicting samples**: Mark field as optional with union of observed types.
- **Unknown date format**: Default to `string` with JSDoc explaining format.

## Resources

- [openapi-typescript](https://openapi-ts.pages.dev/)
- [openapi-fetch](https://openapi-ts.pages.dev/openapi-fetch/)
- [TypeScript Handbook: Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html)
- [json-to-ts VSCode Extension](https://marketplace.visualstudio.com/items?itemName=MariusAlchiworker.json-to-ts)

Related Skills

awesome-copilot-root-typespec-create-agent

16
from diegosouzapw/awesome-omni-skill

Generate a complete TypeSpec declarative agent with instructions, capabilities, and conversation starters for Microsoft 365 Copilot Use when: the task directly matches typespec create agent responsibilities within plugin awesome-copilot-root. Do not use when: a more specific framework or task-focused skill is clearly a better match.

upstash/workflow TypeScript SDK Skill

16
from diegosouzapw/awesome-omni-skill

Lightweight guidance for using the Upstash Workflow SDK to define, trigger, and manage workflows. Use this Skill whenever a user wants to create workflow endpoints, run steps, or interact with the Upstash Workflow client.

upstash/search TypeScript SDK

16
from diegosouzapw/awesome-omni-skill

Entry point for documentation skills covering Upstash Search quick starts, core concepts, and TypeScript SDK usage. Use when a user asks how to get started, how indexing works, or how to use the TS client.

typescript

16
from diegosouzapw/awesome-omni-skill

TypeScript. Proyecto usa este skill; contenido canónico en .ai-system.

typescript-pro

16
from diegosouzapw/awesome-omni-skill

Master TypeScript with advanced types, generics, and strict type safety. Handles complex type systems, decorators, and enterprise-grade patterns. Use PROACTIVELY for TypeScript architecture, type inference optimization, or advanced typing patterns.

typescript-expert

16
from diegosouzapw/awesome-omni-skill

TypeScript and JavaScript expert with deep knowledge of type-level programming, performance optimization, monorepo management, migration strategies, and modern tooling.

defining-typescript-models

16
from diegosouzapw/awesome-omni-skill

Defines standard TypeScript interfaces for Appwrite Collections. Use when creating new models for Tours, Users, or Bookings to ensure full type safety.

pre-configured-apis-rules

16
from diegosouzapw/awesome-omni-skill

Rules for using pre-configured APIs in the project, using them only if they are required by the project.

generating-n8n-workflows

16
from diegosouzapw/awesome-omni-skill

Generates n8n workflow JSON files from user prompts for download and import. Use when user wants to create n8n automation, mentions workflow generation, or needs a .json file for n8n import.

typespec-create-agent

16
from diegosouzapw/awesome-omni-skill

Generate a complete TypeSpec declarative agent with instructions, capabilities, and conversation starters for Microsoft 365 Copilot

claude-typescript-sdk

16
from diegosouzapw/awesome-omni-skill

Build AI applications with the Anthropic TypeScript SDK. Use when creating Claude integrations, building agents, implementing tool use, streaming responses, or working with the @anthropic-ai/sdk package.

ai-apis

16
from diegosouzapw/awesome-omni-skill

How to use AI APIs like OpenAI, ChatGPT, Elevenlabs, etc. When a user asks you to make an app that requires an AI API, use this skill to understand how to use the API or how to respond to the user.