JSON to TypeScript

JSON Sample
TypeScript

How to Generate TypeScript Types From JSON

Paste a representative JSON sample — an API response, a config file, a fixture — into the left panel. Interfaces appear on the right as you type. Set the root name to whatever the payload represents, and every nested interface is named after it, so a document called SearchResponse produces SearchResponseResult and SearchResponseResultAddress rather than three interfaces called Item.

Everything runs in your browser. The sample is never uploaded, which matters more here than for most tools, because the fastest way to get accurate types is to paste a real production response — and real responses contain customer names, internal identifiers, and email addresses.

What the Generator Infers

Load the example and read the output alongside the input. Five inference rules are doing all the work:

In the sampleGeneratedRule
"name": "Ada"name: stringPrimitives map directly
"invitedBy" on one array element onlyinvitedBy?: stringA key missing from some elements is optional
"score": 91.5 and "score": nullscore: number | nullConflicting types union, with null last
"address": { … }address: RootResultAddressNested objects become their own interface
"tags": ["admin","eu"] and "tags": []tags: string[]An empty array yields to a typed sibling

That last rule is worth dwelling on. An empty array carries no type information, so the honest inference is unknown[] — but emitting string[] | unknown[] because one record in your sample happened to have no tags is a type nobody wants. When a typed array appears anywhere alongside an empty one, the empty one is discarded. An array that is empty in every sample still infers as unknown[], because at that point there is genuinely nothing to go on.

Why an Array Sample Beats a Single Object

The single highest-value thing you can do is paste a sample containing several records rather than one. Optionality cannot be inferred from one object: every key it has looks required, and every key it lacks does not exist. Given two records where only the second carries invitedBy, the generator can tell you the field is optional — which is precisely the information that prevents an undefined-property crash three weeks later.

The same applies to nullable fields. A field that is null in one record and a number in another produces number | null, and TypeScript will then force you to handle the null at every use site. If your sample happens to contain only populated records, you get number, and the null arrives in production instead. When in doubt, paste a full page of results.

Identical Shapes Become One Interface

Two objects with the same keys and the same types generate a single interface used in both places. Given {"from":{"x":1,"y":2},"to":{"x":3,"y":4}}, the output declares RootFrom once and types both properties with it, rather than emitting a duplicate called RootTo.

This is what keeps output from a paginated or deeply repetitive payload readable — without it, a response containing the same address shape in four places yields four identical interfaces with numeric suffixes. The trade-off is that the surviving name comes from whichever key was encountered first, so you will sometimes want to rename it. That is a one-line edit, and it is a much better default than deduplicating nothing.

The Four Options

export is on by default, since generated types almost always live in a types.ts that something else imports. Turn it off when you are pasting into the file that uses them.

type instead of interface emits type aliases. Interfaces are the better default — they produce shorter error messages, and they support declaration merging, which is how you extend a generated type without editing the generated file. Choose type when your codebase has standardised on it, or when you intend to build unions and mapped types on top.

readonly properties marks every field immutable. This is the right call for API responses, which you should be treating as immutable snapshots, and the wrong one for a form model you intend to mutate.

unknown over any controls what an empty array or an unknowable value becomes. unknown is strictly safer: it accepts any value on the way in but forces a narrowing check before use, whereas any disables type checking entirely and silently propagates through every expression it touches. Prefer unknown unless you are pasting into a codebase that is not ready for the compile errors it will surface.

What Generated Types Cannot Tell You

A generated type describes your sample, not the API's contract. It is a fast, accurate starting point and nothing more. A field that is nullable in the schema but populated in every record you pasted will be typed non-nullable, and TypeScript will confidently let you dereference it. Where a real schema exists — OpenAPI, JSON Schema, a protobuf definition, a GraphQL SDL — generate from that instead, and use this tool for the endpoints that have none, which in practice is most internal ones.
Types are erased at runtime. Declaring that a response is RootResult[] does not check anything when the response arrives — TypeScript removes the annotation during compilation, and a payload that violates it flows straight through. Types catch mistakes in your code, not bad data from someone else's server. If the boundary needs to be enforced, pair the generated interface with a runtime validator such as Zod or Valibot, and parse at the fetch call.
Dates, big integers, and enums arrive as the wrong thing. JSON has no date type, so an ISO timestamp is typed string and an epoch value is typed number — our Unix timestamp converter is useful for working out which you are looking at. IDs beyond 253−1 are typed number and will already have lost precision during parsing. A status field is typed string where a union of literals such as "active" | "banned" would be far more useful. Each of these is a deliberate narrowing you make after generating.

Where This Fits in a Workflow

Consuming an undocumented internal API. The common case. Capture one response from the network tab, paste, and you have types in seconds instead of hand-writing an interface from a formatted blob. Run the response through our JSON formatter first if it arrived minified.

Typing a config file. Turn tsconfig-style or CI configuration into an interface so a script that reads it is checked at compile time. Config written as YAML can be converted with our YAML to JSON converter first, since the two formats share a data model.

Catching a contract change. Regenerate types against a fresh sample after an upstream deploy and compare them with the ones in your repository. Doing the same check on the raw payloads with our JSON diff tool shows you which field moved, and the regenerated interface shows you whether the change breaks compilation.

Deploying a typed API layer? Edge platforms such as Cloudflare Workers and Vercel run TypeScript directly and type-check on deploy, so a generated interface that stops matching reality fails the build rather than a request. These are affiliate links — they cost you nothing and help keep these tools free.

Frequently Asked Questions

Is my JSON uploaded anywhere?
No. Parsing and type generation run in JavaScript in your browser. Nothing is transmitted, stored, or logged, so a real production response is safe to paste. Your browser's network tab will show no outbound requests while you generate.
What happens if my JSON root is an array or a plain value?
Both work. An array of objects produces an element interface plus an alias — type Root = RootItem[] — so you have a name for the collection and a name for the element. A bare value such as 42 produces type Root = number, which is valid JSON under RFC 8259 and valid TypeScript.
How are keys that are not valid identifiers handled?
They are quoted. A key like content-type or 2fa becomes "content-type": string and "2fa": boolean, which is legal TypeScript and preserves the key exactly. Renaming it would produce a type that no longer describes the payload.
Why did an array of mixed values become a parenthesised union?
Because number | string | null[] would mean something different — it parses as a number, or a string, or an array of nulls. Wrapping it as (number | string | null)[] is the only way to express an array whose elements may be any of the three.
Can it generate JSON Schema or types for another language?
Not currently — this tool emits TypeScript only. The inference rules it applies, particularly merging array elements to discover optional keys, transfer directly to other languages if you are writing the equivalent by hand.
Should generated types be committed to the repository?
Yes, if you generated them from a sample rather than a schema. They are ordinary source you will edit — narrowing string fields to literal unions, correcting nullability the sample missed. Committing them means those corrections survive, and it means a diff shows you when a regenerated type stops matching.