Skip to content
ZeroServer.tools

JSON to TypeScript

Generate TypeScript interfaces from any JSON — handles arrays, nulls, and optional keys.

Drop a JSON file or click to browseLoads it into the input below
Output updated — 217 characters

JSON to TypeScript interface generation

TypeScript interfaces provide compile-time type safety for JSON data structures. This tool recursively inspects your JSON — detecting strings, numbers, booleans, nulls, arrays, and nested objects — and emits a matching set of interface declarations.

Customize formatting styles with advanced configuration controls: toggle property optionality (infer based on key presence, make all optional, or force all required), prepend readonly modifiers for read-only immutable patterns, and choose whether to apply standard Hungarian notation prefixing (prepending I to interface names).

Related tools: JSON to Go Struct · JSON Formatter · JSON Schema Generator

Built and maintained by Meet Shah · Last updated

What this tool is used for

  • Generating interfaces from a real API response instead of hand-typing a large payload.
  • Producing a starting type for an endpoint that has no published schema.
  • Checking whether a field you assumed was a string is actually a number in practice.
  • Creating types for a fixture so tests fail when the shape changes.
  • Comparing generated types across two API versions to spot a breaking change.

How it works in practice

A worked example

An endpoint returns an array whose objects are not all the same shape, and the front end needs a type that admits both.

Input
[
  { "name": "Alice", "age": 30, "email": "[email protected]", "scores": [95, 87, 92],
    "address": { "city": "New York", "zip": "10001" }, "active": true },
  { "name": "Bob", "age": null, "scores": [78],
    "address": { "city": "London", "zip": "E1 1AA" } }
]
Output
interface RootItem {
  name: string;
  age: number | null;
  email?: string;
  scores: number[];
  address: Address;
  active?: boolean;
}

interface Address {
  city: string;
  zip: string;
}

type Root = RootItem[];

The two objects were merged rather than turned into a union, and three decisions came out of that. A key present in both stays required; a key present in only one becomes optional, which is why the email and the flag carry question marks. A key present in both but null in one becomes a union with null instead of being made optional — those are different statements and the tool keeps them apart. The repeated nested object was lifted into its own named interface rather than inlined twice, and the array itself gets an alias so the element type has a name you can import.

The edge case that catches people

Every one of those decisions is a guess from a sample, and a sample cannot tell you what is possible — only what happened. Two records are enough to make a field optional and not nearly enough to prove another one never is, so a type inferred from a small response is systematically too permissive about what it has seen and too strict about what it has not. The array of numbers is the clearest case: it says numbers because both arrays held numbers, and one string in the next response would make it wrong.

When not to use this tool

If the API has a schema, generate from the schema. An OpenAPI document, a GraphQL schema or a protobuf definition states what the server promises, including the fields your sample happened not to contain and the ones that are nullable in principle — and a generator that reads one will produce types that stay correct when the response changes. Infer from a payload when there is no schema, or to get a first draft quickly, and treat the result as something to check against the documentation rather than as the documentation.

Frequently Asked Questions

Interface or type alias?
Interfaces for object shapes, because they support declaration merging and produce clearer error messages. Type aliases for unions, intersections and anything that is not an object — the two are otherwise near-equivalent for this purpose.
How is an array of mixed types handled?
By unifying across every element rather than trusting the first. `[1, null, 3]` should not become `number[]` because it starts with a number, and a genuinely mixed array becomes a union — reading only element zero is the classic bug in generators like this.
Why is a JSON null typed as nullable rather than as null?
Because a null in a sample usually means the field is optional, not that it is always null. The generator cannot tell which, so the honest output is a union — and deciding between `T | null` and `T?` is a judgement the sample cannot make for you.
Do generated types actually guarantee anything?
No. They describe what one sample looked like, and TypeScript erases them at runtime — so an API that changes shape produces no error at the boundary, only a confusing failure later. Runtime validation with Zod or similar is what closes that gap.
How should snake_case keys be handled?
Keep them in the type, because that is what the API actually sends. Renaming to camelCase in the type without a transformation layer produces a type that lies about the data, which is worse than an unfashionable property name.

Common errors and gotchas

  • Treating every field as required. One sample cannot distinguish an absent field from an optional one.
  • Accepting a `null` inferred as its own type when the API means the field is nullable.
  • Losing union variety in arrays, where the type is usually taken from the first element only.
  • Shipping generated names unchanged, which produces deeply nested interfaces nobody wants to read.
  • Assuming a large integer is safe as a number. Above 2^53 it should be a string, and the sample will not tell you.

Related Developer Utilities tools

Private & free — this tool runs entirely in your browser.

IndieKitShip your Next.js startup in days.affiliate