TypeScript
Developer Guide

JSON to TypeScript: 4 Approaches Compared (2026)

Every TypeScript developer who consumes APIs eventually asks the same question: how do I get types for this JSON? There are four real answers in 2026, each with different trade-offs in accuracy, maintenance cost, and runtime safety. Here's how they compare — with working code for each.

April 29, 2026
11 min read
PDF Mavericks Team

The Example JSON

We'll use the same JSON response across all four approaches so you can see exactly what each produces:

{
  "user": {
    "id": 42,
    "name": "Jane Doe",
    "email": "jane@example.com",
    "roles": ["admin", "editor"],
    "preferences": {
      "theme": "dark",
      "notifications": true
    },
    "createdAt": "2024-01-15T09:30:00Z"
  }
}

This covers the common patterns: nested objects, arrays, booleans, numbers, and ISO date strings — everything you encounter in a real API response.

Approach 1: Manual Typing

Write the interfaces yourself, guided by the JSON structure. Time-consuming for large responses, but gives you full control over naming and documentation.

interface UserPreferences {
  theme: string
  notifications: boolean
}

interface User {
  id: number
  name: string
  email: string
  roles: string[]
  preferences: UserPreferences
  createdAt: string // ISO 8601 date string
}

interface ApiResponse {
  user: User
}

Pros

  • Full naming control
  • Add JSDoc comments
  • No dependencies
  • Handle optional fields explicitly

Cons

  • Tedious for large responses (50+ fields)
  • No runtime validation
  • Drift when API changes
  • Easy to miss optional fields

Best for: small, stable APIs where you want total control and the JSON has 10 fields or fewer.

Approach 2: quicktype (CLI + Online)

quicktype.io is the most widely used JSON-to-types tool in 2026. It infers TypeScript interfaces, Zod schemas, io-ts codecs, and types for 20+ languages from sample JSON. The npm package (quicktype v23.2.6) is the same engine as the website.

Generated output for our example JSON:

// Generated by quicktype (quicktype.io)
export interface Root {
  user: User
}

export interface User {
  id:          number
  name:        string
  email:       string
  roles:       string[]
  preferences: Preferences
  createdAt:   Date
}

export interface Preferences {
  theme:         string
  notifications: boolean
}

CLI usage:

# Install quicktype globally
npm install -g quicktype

# Generate TypeScript interfaces from a JSON file
quicktype --lang ts --src response.json --out types.ts

# Generate from a URL (quicktype fetches the JSON)
quicktype --lang ts --src-lang json \
  https://api.example.com/user/42 --out types.ts

# Generate with Zod schemas instead of plain interfaces
quicktype --lang ts --src response.json \
  --framework zod --out types.ts

Pros

  • Handles complex nested structures automatically
  • 20+ output languages
  • CLI integrates into build pipelines
  • Can merge multiple sample inputs for better inference

Cons

  • Infers from samples — optional fields need multiple samples
  • Generated code is verbose
  • quicktype-core npm package is 6MB
  • Still no runtime validation in plain interface mode

Approach 3: Zod Schema-First

Zod (34K+ GitHub stars as of April 2026) inverts the problem: define a schema, derive the TypeScript type from it. This is the approach used in most production TypeScript codebases in 2026 because it gives you runtime validation for free.

import { z } from 'zod'

const PreferencesSchema = z.object({
  theme: z.string(),
  notifications: z.boolean(),
})

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.email(),
  roles: z.array(z.string()),
  preferences: PreferencesSchema,
  createdAt: z.string().datetime(),
})

// TypeScript type inferred from schema
type User = z.infer<typeof UserSchema>

// Validate at runtime
const result = UserSchema.safeParse(apiResponse)
if (result.success) {
  // result.data is fully typed as User
}

The key insight: z.infer<typeof UserSchema> produces the TypeScript type automatically. You write the schema once and get both validation and types from it.

Pros

  • Runtime validation — catches API changes at the boundary
  • Types derived from schema, always in sync
  • Excellent error messages
  • 8KB gzip — production-viable

Cons

  • You still have to write the schema manually
  • Performance overhead on large arrays
  • Adds a dependency

Combination approach: Use quicktype to generate a starter Zod schema (--framework zod), then refine it manually. Best of both worlds.

Approach 4: ts-toolbelt DeepRequired

ts-toolbelt (6.5K GitHub stars) provides utility types for deep transformation of existing types. If you already have a base interface but need to ensure all nested fields are required or optional, ts-toolbelt's O.Required and O.Optional handle it at the type level.

import type { O } from 'ts-toolbelt'

// Make all nested fields required
type StrictUser = O.Required<User, PropertyKey, 'deep'>

// Make all nested fields optional
type PartialUser = O.Optional<User, PropertyKey, 'deep'>

// Pick specific nested fields
type UserSummary = O.Pick<User, 'name' | 'email'>

ts-toolbelt isn't a JSON-to-types tool per se — it's a type transformation library. Its main value here is refining generated or manually-written interfaces, not replacing them.

Comparison Table

ApproachTime to TypesRuntime ValidationHandles Optional FieldsDependencies
Manual typing10-30 minNoManualNone
quicktype30 secondsNo (or with Zod flag)Inferred from samplesquicktype-core (6MB)
Zod schema5-15 minYesExplicit z.optional()zod (8KB gzip)
ts-toolbeltN/A (modifier only)NoVia O.Optionalts-toolbelt (types only)

Which One Should You Use?

Quick prototype or one-off script

Use quicktype. Paste JSON, get types in 30 seconds, move on.

Production app consuming external APIs

Zod schema-first. External APIs change without notice. Runtime validation at the API boundary catches breaking changes before they propagate through your app.

Large API with 50+ field response

quicktype to generate the starter Zod schema (--framework zod), then manually mark optional fields and add business-logic refinements.

Internal API you control

Share types from source rather than generating them. A shared types package or tRPC eliminates the JSON-to-types problem at the root.

Explore JSON in the Browser

Before generating types, use our JSON formatter to explore your API response structure, format it for readability, and validate the syntax.

Browser-only — your API response data stays local. TypeScript type generation is on our roadmap.

Open JSON Formatter