Skip to content

JSON to TypeScript

Generate TypeScript interfaces from a JSON sample.

Input

What this tool does

Generate TypeScript interfaces from a JSON sample. Paste a payload — an API response, a config blob, a webhook body — and get strongly-typed interface declarations you can drop into a project. Array element types are inferred, nested objects become their own interfaces, nullable fields are emitted as a union with null. Powered by quicktype, runs entirely in your browser; no payload leaves the page.

How to use it

Paste JSON (or load the example), pick a top-level type name, and read the TypeScript on the right. Switch to a different language at any time via the tabs above — the same input regenerates in place.

Input: {"id":42,"name":"devsmiths","createdAt":"2024-03-11T08:24:00Z","stars":1280,"public":true,"contributors":[{"login":"ada","commits":51,"admin":true},{"login":"linus","commits":33,"admin":false}],"homepage":null}

Output (TypeScript):

export interface Root {
    id:           number;
    name:         string;
    createdAt:    string;
    stars:        number;
    public:       boolean;
    contributors: Contributor[];
    homepage:     null;
}

export interface Contributor {
    login:   string;
    commits: number;
    admin:   boolean;
}

Limits and edge cases

  • Output uses interface by default. If you need a type alias instead (e.g. for union types or mapped types), toggle just types in the options pane — type output is closer to a structural definition and lets you compose with utility types later.
  • Fields where the sample contains null are typed as the literal null, not null | string. The generator has only one sample to work from — it doesn’t know the field is sometimes a string. Widen by hand, or pass all optional in the options pane.
  • TypeScript output is the only target with full CodeMirror syntax highlighting. Other languages render as plain monospace text to keep the page bundle small; Copy / Download still produce correct files.
  • The generator does not emit runtime validators (no Zod, Valibot, or io-ts). If you need to validate untrusted JSON at runtime, generate the interfaces here and write a validator alongside, or use a schema-first tool like quicktype with Zod output.
  • Single-sample inference: every field present in your sample becomes part of the type. Fields absent from the sample don’t become optional automatically — toggle all optional if every field should be nullable.
  • Background reading: JSON to TypeScript in practice walks through the rename / tighten step end-to-end.

Frequently asked questions

Should I use interface or type — and how do I switch?
Default is interface. Use interface for plain object shapes you might extend later; use type for unions, intersections, mapped types, or tuple shapes. To switch, toggle 'just types' in the options pane — the generator emits `type Root = { ... }` instead of `interface Root { ... }`. Note that you can't extend a type with declaration-merging the way you can interface, so if you're shipping a public API surface, interface is usually the right pick.
Why is my field typed as null instead of string | null?
Because the sample only showed null for that field, and the generator has nothing else to go on. With a second sample containing a string value, quicktype would widen to `string | null`. Workarounds: paste a sample where the field has a real value (the resulting interface keeps `null` only if you add it), or change the literal `null` to `string | null` by hand.
Can I generate Zod / Valibot schemas alongside the interfaces?
Not from this UI. The output is type-only; runtime validators are out of scope to keep the generator deterministic. For Zod, generate interfaces here and write the schemas alongside (a 1:1 mapping you can refactor with a snippet), or use quicktype's CLI with `--lang TypeScriptZod` for a one-shot generation.
How does the generator handle JSON keys that aren't valid TS identifiers?
Keys with hyphens, leading digits, or special characters are emitted in quotes (`'2024-tax-year': number`) and accessed via index syntax (`r['2024-tax-year']`) at the call site. TypeScript is happy with quoted keys; the generated interface is structurally valid even when the JSON uses cursed key names.
Why is the first generation slow?
The quicktype engine (~700 KB compressed) is loaded into a Web Worker on first use. After that it stays cached for the rest of the session — subsequent generations are near-instant. The Worker means the engine never blocks the UI thread, so paste-and-tab-away keeps working at full speed.
Does the output have correct line wrapping for prettier / eslint?
It's pre-formatted at column ~100 with 4-space indent. Prettier with default settings will reformat to 2-space indent and column 80; that's a no-op semantic change. Drop the output through `prettier --write` if you want it to look like the rest of your codebase before committing.

Content reviewed by