Generate TypeScript Interfaces from JSON (The Complete Guide)

Typing an API response by hand is tedious and easy to get wrong: you squint at a payload, guess at which fields are optional, and forget half of them by the time you reach the nested objects. Generating the TypeScript interfaces straight from a real JSON sample removes the guesswork, and the types match the data exactly. This guide covers how that generation works, the fastest way to do it, how inference handles the tricky cases, and the gotchas to watch for.

The fastest route: a browser generator

For a one-off, paste the JSON into the JSON to TypeScript converter. It reads the sample, infers the type of every field, and writes clean interfaces: nested objects become their own interfaces, arrays are typed by their element, and optional fields and unions are detected automatically. You name the root interface and choose whether to add export. Because it runs entirely in your browser, the JSON never leaves your machine, which matters when the sample is a real production response.

If the JSON will not convert, it is almost always invalid rather than genuinely unconvertible. Run it through the JSON parser to find the exact syntax error, or format it so you can see the structure first.

How the inference works

A generator walks the JSON and makes the same handful of decisions every time. Knowing them tells you what to expect, and why.

Here is the shape of a simple conversion:

{ "id": 1, "name": "Ada", "tags": ["admin", "dev"] }

becomes:

export interface Root {
  id: number;
  name: string;
  tags: string[];
}

Optional fields: the part that matters most

The single most useful thing a generator does is detect optional fields, and it can only do that from an array of objects. When several objects share a shape but some omit a key, that key is optional:

[
  { "sku": "J-01", "qty": 2 },
  { "sku": "J-07" }
]
export interface Root {
  sku: string;
  qty?: number;
}

qty is marked optional with ? because the second record omits it. This is why you should paste a representative sample with several records, not a single object: one object alone cannot reveal which fields are sometimes missing. The more varied the sample, the more accurate the interface.

Null and union types

JSON has no way to say “string or null”, so a generator infers it from the data. A field that is null in the sample is typed null; a field that is sometimes a string and sometimes null becomes a union:

coupon: string | null;

The same logic applies to arrays of mixed types. [1, "a"] becomes (number | string)[]. Unions keep the interface honest: it describes every value actually seen, rather than silently picking one.

When you see any

An empty array or object gives the generator nothing to infer from, so it falls back to any[] or any. That is not a bug, it is a lack of evidence. The fix is to paste a sample where those fields are populated. If a field is genuinely freeform, any (or unknown, which you can tighten to by hand) is the honest type. Expanding the payload in the JSON viewer first helps you spot which fields come back empty.

Doing it with libraries

For a one-off, the browser tool is quickest. For a repeatable build step, two approaches are common:

A rule of thumb: generate from a sample when the shape is defined only by example data, and generate from a schema when you have one, since a schema encodes optionality and constraints that a single sample cannot.

The gotchas that bite

Frequently asked questions

How do I generate TypeScript interfaces from JSON?
Paste the JSON into the JSON to TypeScript converter and it writes the interfaces, inferring nested types, arrays, optional fields, and unions. For a build step, quicktype does the same from the command line.

How does it know which fields are optional?
From an array of objects: a key present in some records but missing from others is marked optional with ?. A single object cannot show optionality, so always paste a representative, multi-record sample.

What happens to null values?
A null is typed null, and a field that is sometimes a value and sometimes null becomes a union such as string | null, so the interface matches every value in the sample.

Should I generate from JSON or from a JSON Schema?
Generate from a sample when example data is all you have; generate from a schema (with json-schema-to-typescript) when you have one, because a schema encodes optionality and constraints a single sample cannot.

For the format underneath all this, see the guide on what JSON is, the practical parse JSON in JavaScript walkthrough, and the roundup of free JSON tools for the rest of the toolkit.

Leave a Reply

Your email address will not be published. Required fields are marked *