How JSON-to-TypeScript inference works
Inference is a single pass over the parsed JSON tree. The tool reads each value, picks a TypeScript type for it, and then writes out one interface per object it found.
- Parse the JSON sample with the browser's native parser and reject malformed input with a line/column hint.
- Sniff a TypeScript type for each value —
string,number,boolean,null, array, or nested object. - Give every nested object an interface name derived from its parent property key (so
user.addressbecomes anAddressinterface). - Merge item types across each array so a list of
{id: 1}and{id: 2, label: "x"}produces a union with the right optional fields. - Apply your options (interface vs. type, readonly, optional-nullable) and emit declarations in dependency order so the file compiles without forward references.
Why generate TypeScript types from JSON?
- Most shape bugs are catchable at compile time if the response type is written down. Inferring an interface from a real payload writes most of it for you, and `strict` mode catches the field the docs forgot to mention.
- Pairing inferred interfaces with a runtime validator like Zod or io-ts gives the same shape two jobs: editor autocomplete in development and a 400 at the edge when production sends something unexpected.
- TypeScript's language server only surfaces fields it knows about. Once you import the inferred interface, autocomplete works the moment you type the dot — no more `as any` cast on the response and a frustrated grep across the repo.
- If you're about to write an OpenAPI spec, an inferred interface is a fast first draft of the response schema. You'll still want hand-written examples and constraints, but the property names and types are already correct.
Common applications
Inference helps most when a real payload exists but a schema doesn't.
- Typing third-party webhook payloads from Stripe, GitHub, or Twilio before writing a handler.
- Bootstrapping types for an internal REST API so the frontend team can start coding against it the same day the backend lands.
- Generating a starting point for a Zod, io-ts, or Valibot schema from an observed API response.
What does the output look like?
Given a sample JSON document and a root name, the generator produces a tree of interfaces, one per nested object. For the input below with root name User:
Paste {"id":1,"name":"Alice","tags":["a","b"],"address":{"city":"Paris"}} with root name User and the generator produces:
export interface User {
id: number;
name: string;
tags: string[];
address: Address;
}
export interface Address {
city: string;
}
Notice that address got promoted to its own named interface — that's the dependency-ordered output. Same JSON with the type declaration style instead would emit export type User = {...}; with the readonly toggle on, every property gets the readonly modifier.
Generator options
Declaration style
Pick interface (the standard TypeScript idiom for object shapes) or type (handy if you'll need mapped types, conditional types, or intersections later). Both produce identical runtime behaviour; the choice is a coding-style preference.
Optional nullable fields
When a sampled value is null, the field's type becomes T | null. Turning this option on also adds a ? modifier so the field is optional on the TypeScript side — useful when the API sometimes omits the key entirely instead of returning null.
Readonly modifier
Prepends readonly to every property declaration so the emitted interface matches an immutable data model. Handy for Redux state slices, frozen API responses, or anywhere you want the compiler to flag accidental mutation.
Does this support nested objects and arrays?
Yes. Every nested object becomes a named interface derived from its parent property key, and arrays infer the item type from their contents. Arrays of objects get an interface per object shape, with union types where the shapes disagree.
How are optional fields inferred?
Turn on the “Mark null-able fields optional” toggle and any field whose sampled value is null gets a ? modifier on the key plus | null in the type. Without the toggle, the field stays required and the type is just T | null.
Does this support discriminated unions?
Basic union types come out when an array holds mixed-shape items or when a field carries both a value and null. Full discriminated-union inference (picking type or kind as the tag and splitting variants) needs multiple samples — that's planned but not in today's build.
Can I infer types from multiple JSON samples?
Not yet — today's inferrer reads one sample at a time. If you have two payloads that should share an interface (say, a list endpoint and a single-item endpoint), the practical workaround is to merge them into one array, generate from that, and then rename the resulting union types. Multi-sample inference is on the roadmap because it's the only way to spot fields that are present in one response and absent in another.
Paste a payload, name the root, copy the interfaces. The whole pipeline runs in your browser, so an unreleased API or a signed webhook body stays on your machine.