Generate TypeScript Types from APIs

VerifiedCaution

Generates TypeScript interfaces from API responses or OpenAPI schemas. Use to type API responses, create interfaces from JSON, or keep types in sync.

Sby Skills Guide Bot
DevelopmentIntermediate
7/25/2026
Claude CodeCursorWindsurfCopilot
#typescript#api-types#openapi#code-generation#type-inference

Recommended for

Automated summary

Generates TypeScript interfaces from API JSON responses or OpenAPI/Swagger schemas, following explicit type-inference rules and adding JSDoc comments.

Strengths

  • Ships a clear inference table (null, empty arrays, ISO dates, UUIDs) that removes guesswork
  • Handles tricky shapes: nested objects, arrays of objects, fixed-length tuples, and optional fields detected across multiple samples
  • Suggests reusable branded types (UUID, Email, ISODateString) for semantically meaningful strings
  • Routes by source type: pasted JSON, OpenAPI, GraphQL, or a live endpoint

Limitations

  • Output quality hinges on the samples provided: a partial payload yields incomplete types or wrong nullability
  • No runtime validation is generated (no Zod or similar), so types vanish at runtime
  • Branded types need manual wiring in the parsing layer to actually constrain values
When to use it

Reach for it when you have a JSON payload or OpenAPI spec in hand and want typed, documented TypeScript interfaces quickly.

When not to use it

Skip it when you need runtime validation or types auto-synced in CI, where a generator wired to the source schema fits better.

Security analysis

Caution
Quality score88/100

The skill is benign TypeScript type-generation guidance, but it does involve legitimate use of network, npm, and npx commands. Because those tools can execute remote packages and fetch external resources, it warrants caution rather than a pure safe rating.

Findings
  • •No allowed-tools are declared, but the skill includes shell commands (`npm install`, `npx`) and recommends fetching live endpoints/remote OpenAPI schemas.
  • •The `npx openapi-typescript <url>` examples use an unpinned package and remote URL input, so the agent may execute network-fetched tooling and process untrusted schemas.
  • •No destructive, exfiltration, or obfuscated behavior is present.
How this analysis is made

Examples

Type a JSON payload
Here's a sample response from our /users endpoint. Generate TypeScript interfaces for it, with nested objects split into their own types and JSDoc on each field.
From OpenAPI spec
Generate TypeScript types from this OpenAPI schema for the orders and line-items endpoints, and put them in types/api/.
Nullable and optional fields
I have three sample responses from the same endpoint — infer which fields are optional or nullable and generate a single interface that covers all three.

name: generating-typescript-types-from-apis description: 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.

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:

{
  "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:

// 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:

// 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:

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

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

Array of objects:

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

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

Mixed array (avoid if possible):

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

Tuple (fixed length, known types):

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

Step 5: Handle Optional Fields

Detect optional fields from multiple samples:

// 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:

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:

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

// Usage
type UsersResponse = PaginatedResponse<User>;

API envelope:

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:

npm install -D openapi-typescript
# 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:

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:

npm install openapi-fetch
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

// 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:

# .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:

# .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:

// 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
# 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

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/generate-typescript-types-from-apis-mrzxuqg8/download' --output '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md.part' && mv '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md.part' '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md' && test -s '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/generating-typescript-types-from-apis' && cp -R '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8' '.claude/skills/generating-typescript-types-from-apis'

Cursor
test -f '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md' && test -s '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/generating-typescript-types-from-apis' && cp -R '.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8' '.cursor/skills/generating-typescript-types-from-apis'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/generate-typescript-types-from-apis-mrzxuqg8'

openskills read 'generate-typescript-types-from-apis-mrzxuqg8'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/generate-typescript-types-from-apis-mrzxuqg8/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills