Skip to the tool
DevToolBench

JSON to TypeScript Converter

JSON in, TypeScript interfaces out — unknown, never any.

0 chars
Types
0
Lines
0
Characters
0
Valid JSON

Everything runs in your browser. Nothing you type is sent to a server.

Found a bug in this tool? Report it.

Share this tool

What the generator does

This JSON to TypeScript converter reads one JSON document and writes the interfaces that describe it, entirely in your browser. Nested objects become their own named interfaces, taken from the key that held them; arrays of objects are merged into a single shape and the key is singularised, so users produces a User. Nothing is uploaded, and nothing needs a build step.

You choose the root name, whether declarations come out as interface or type, and whether every property is marked readonly.

The sample is not the schema

This is the single thing worth understanding before trusting the output. What comes back describes the exact bytes you pasted and makes no claim about the endpoint that produced them.

Three ways that bites. A field that happened to be null in this response is typed null, when the real contract is almost certainly nullable-and-sometimes-a-string. A field that happened to be absent from every sampled object simply does not appear. And a numeric field that has only ever held whole numbers gives you no hint that it is a currency amount with two decimal places waiting somewhere in production.

Pasting several responses at once helps with the second problem: when a key is present in some objects of an array and missing in others, it is marked optional with ?, which is exactly the signal you want. Pasting one lucky response and shipping the result is how a type ends up lying confidently for six months.

Interface, type alias, and which to reach for

An interface can be declared twice and the declarations merge. That is indispensable when you are adding a property to something a library exported, and a quiet disaster when two files in your own project define User and neither author notices. A type alias cannot merge, which makes the collision an error at the point it happens.

Beyond that, aliases do things interfaces cannot express at all: unions, intersections of computed shapes, mapped and conditional types. Teams that want one rule rather than two usually land on type for that reason. Teams that write a lot of ambient declarations land on interface. Neither choice will be the thing that hurts you.

Why unknown and not any

Every gap in the generated output is filled with unknown: empty arrays, and values the document never demonstrated. That is deliberate. any silences the compiler everywhere the value travels, so one unfinished corner of a type quietly disables checking across half a call chain. unknown does the opposite — it refuses to be used until you narrow it, which keeps the unfinished part of the model on screen where you can finish it.

The step after this one

Types describe; they do not check. Once the shape is right, put a runtime validator at the boundary where the JSON actually arrives — Zod, Valibot, ArkType, or a hand-written guard for something small. Declare the schema once, infer the TypeScript type from it, and the two can never drift, because there is only one of them. The output here is a good first draft of that schema: the field names and the nesting are already correct, and what you add is the part a sample could never tell you — formats, ranges, and which fields are genuinely optional.

If the payload you are working from is YAML rather than JSON, the JSON to YAML converter goes the other way, and the XML formatter will make an unreadable XML response legible before you decide what to do with it.

Frequently asked questions

Why did a field come out as null instead of string | null?

Because that response had null in it and nothing else. The generator describes the document you pasted, not the endpoint behind it — it has never seen the row where the avatar is filled in. Widening that field to string | null is a decision only you can make, and it is the first edit most people make to the output.

Should I pick interface or type?

For an object shape, either works and the difference is small. Interfaces merge when declared twice, which is what you want for augmenting a library's types and what you never want by accident in your own code. Type aliases cover things interfaces cannot express — unions, mapped types, template literals — so a codebase that standardises on one usually standardises on type. Both are offered above because both house styles are common.

Why unknown[] for an empty array rather than any[]?

An empty array carries no information about what belongs in it, and any is a promise that the compiler will stop asking. unknown forces you to narrow before use, so the unfinished part of the type stays visible in the editor instead of disappearing. If you already know what goes in there, replacing unknown takes one keystroke more than replacing any.

Do the generated types check anything at run time?

No. TypeScript types are erased at compile time, so an interface describing an API response is a comment the compiler can read — nothing verifies that the JSON arriving at three in the morning still matches it. That is what a runtime validator is for, and it is why the section on Zod below exists.

Why is one of my keys wrapped in quotes?

Because it is not a valid TypeScript identifier. Keys such as user-agent, 2fa or content type contain characters an identifier cannot, so they are emitted as quoted property names. They still work; you just have to read them with bracket access rather than a dot.

Related tools

Updated