Tutorial2026-03-118 min read

JSON to TypeScript: Auto-Generate Type-Safe Code from Any JSON

The Problem: Untyped JSON in TypeScript Projects

TypeScript's greatest strength is its type system. It catches bugs at compile time, enables intelligent autocomplete, and serves as living documentation for your codebase. Yet one of the most common patterns in web development — fetching JSON from an API — often bypasses the type system entirely.

Consider a typical API call:

const response = await fetch("https://api.example.com/users/1");
const user = await response.json(); // type: any

The moment you call .json(), TypeScript gives up. The returned value is typed as any, which means you lose all type checking, autocomplete, and refactoring support. You might accidentally access user.nmae instead of user.name, and TypeScript will not warn you until your application crashes at runtime.

The solution is to define TypeScript interfaces (or types) that describe the shape of your JSON data. But writing these types by hand is tedious, error-prone, and time-consuming — especially for deeply nested or complex API responses.

Manual Type Definitions: The Traditional Approach

Before automated tools existed, developers wrote TypeScript types by hand based on API documentation or sample responses. Here is an example:

// API response sample:
// {
//   "id": 1,
//   "name": "John Doe",
//   "email": "john@example.com",
//   "address": {
//     "street": "123 Main St",
//     "city": "New York",
//     "zipcode": "10001"
//   },
//   "roles": ["admin", "editor"]
// }

interface Address {
  street: string;
  city: string;
  zipcode: string;
}

interface User {
  id: number;
  name: string;
  email: string;
  address: Address;
  roles: string[];
}

This approach works for simple structures, but it quickly becomes impractical for large APIs with dozens of endpoints. You also have to manually keep the types in sync whenever the API evolves — a process that is both boring and error-prone.

Automated Type Generation

Automated tools analyze a JSON sample and infer the appropriate TypeScript types from the data. They handle edge cases like nullable fields, union types, and deeply nested structures that are easy to miss when writing types by hand.

One of the most popular open-source tools for this purpose is quicktype. It powers many JSON-to-code converters and supports a wide range of target languages beyond TypeScript.

How It Works

The type inference process follows these steps:

  1. Parse the JSON input into an abstract syntax tree
  2. Analyze each value to determine its type (string, number, boolean, null, object, or array)
  3. Infer structure — group related keys into interfaces, detect arrays of uniform objects, and identify optional fields
  4. Generate idiomatic code in the target language with proper naming conventions

Step-by-Step: Converting an API Response

Let's walk through a realistic example. Suppose you are building an e-commerce application and your product API returns the following JSON:

{
  "id": 42,
  "name": "Wireless Headphones",
  "price": 79.99,
  "inStock": true,
  "tags": ["electronics", "audio", "wireless"],
  "specs": {
    "brand": "AudioTech",
    "weight": "250g",
    "batteryLife": "30 hours",
    "connectivity": ["Bluetooth 5.3", "3.5mm jack"]
  },
  "reviews": [
    {
      "userId": 101,
      "rating": 5,
      "comment": "Great sound quality!",
      "date": "2025-12-01"
    },
    {
      "userId": 202,
      "rating": 4,
      "comment": null,
      "date": "2025-12-15"
    }
  ]
}

An automated tool will analyze this JSON and produce TypeScript interfaces like:

export interface Product {
  id:      number;
  name:    string;
  price:   number;
  inStock: boolean;
  tags:    string[];
  specs:   Specs;
  reviews: Review[];
}

export interface Specs {
  brand:        string;
  weight:       string;
  batteryLife:  string;
  connectivity: string[];
}

export interface Review {
  userId:  number;
  rating:  number;
  comment: string | null;
  date:    string;
}

Notice how the tool correctly handles several non-trivial cases:

  • Nested objects (specs) are extracted into their own interfaces
  • Arrays of objects (reviews) are typed as Review[] with a new interface for the element type
  • Nullable fields (comment is null in one review) are inferred as string | null
  • Primitive arrays (tags, connectivity) are typed as string[]

Beyond TypeScript: Multi-Language Support

While TypeScript is the most common target, JSON-to-code tools typically support many other languages. Here is the same product JSON converted to different languages:

Go

type Product struct {
    ID      int64    `json:"id"`
    Name    string   `json:"name"`
    Price   float64  `json:"price"`
    InStock bool     `json:"inStock"`
    Tags    []string `json:"tags"`
    Specs   Specs    `json:"specs"`
    Reviews []Review `json:"reviews"`
}

Python (dataclass)

@dataclass
class Product:
    id: int
    name: str
    price: float
    in_stock: bool
    tags: List[str]
    specs: Specs
    reviews: List[Review]

Swift

struct Product: Codable {
    let id: Int
    let name: String
    let price: Double
    let inStock: Bool
    let tags: [String]
    let specs: Specs
    let reviews: [Review]
}

Each language output follows that language's idiomatic conventions: Go uses struct tags for JSON field mapping, Python uses snake_case naming, and Swift adopts Codable for serialization.

Best Practices

1. Use Multiple Samples When Possible

A single JSON sample may not reveal all possible shapes of the data. For example, a field that appears as a string in one response might be null in another. When possible, feed multiple representative samples to get more accurate types.

2. Review and Refine Generated Types

Auto-generated types are a strong starting point, but they may not capture business-level constraints. For instance, a status field generated as string might be better expressed as a union type:

// Generated
status: string;

// Refined
status: "pending" | "active" | "cancelled";

3. Handle Optional vs Nullable Correctly

There is an important distinction between optional fields (that may be absent from the response) and nullable fields (that are always present but may have a null value):

// Optional: the key might not exist
middleName?: string;

// Nullable: the key exists but value can be null
comment: string | null;

// Both optional and nullable
nickname?: string | null;

4. Keep Generated Types in a Dedicated Directory

Organize your generated types in a dedicated folder such as src/types/api/ or src/generated/. This makes it clear which types are auto-generated and may be overwritten, versus hand-written types that contain custom logic.

Conclusion

Manually writing TypeScript interfaces for every JSON response is a chore that slows you down and introduces risk. Automated JSON-to-code tools eliminate the tedium, handle edge cases like nullable and nested types, and let you focus on building features instead of writing boilerplate.

Whether you are bootstrapping a new project, integrating a third-party API, or migrating a JavaScript codebase to TypeScript, auto-generating types from real JSON data is one of the fastest ways to improve your developer experience.

JSON to Code Generator — Paste your JSON and instantly generate TypeScript, Go, Python, Swift, Java, or Kotlin code. No installation required.

JSON to Code Generator

Generate TypeScript, Go, Python, and more from JSON

Try it now

Related Posts