JSON to TypeScript Types
Generates TypeScript interfaces from a JSON sample by merging every array element, so keys missing from some objects become optional rather than being ignored. Emits valid output for array roots, names nested interfaces, and reports what it could not infer.
🔒 This tool runs entirely in your browser. Your files are never uploaded to a server.
Generated in this page — nothing is uploaded, which matters when the sample JSON is a real API response. Remember that these types describe the sample you pasted, not the API's contract: a field absent from your sample cannot appear in the output.
How to use it
- Paste a JSON response — the more representative the sample, the better the types.
- Include several array elements if the objects vary, so optional fields are detected.
- Read the notes underneath: they say what could not be inferred.
- Copy the types, then check the optional markers against your real API documentation.
Merging is the whole job
Most generators look at the first element of an array and assume the rest match. Real API responses do not work that way — the interesting information is in the differences. This tool observes every value at every path and merges the shapes, which is why the sample above produces this:
verified?: boolean present in one object of two
lastSeen: string | null null in one, a string in the other
tags: string[] populated in one, empty in the other
Each of those three lines is a case a first-element-only reader gets wrong. It would mark verified
absent entirely, type lastSeen as just
null, and — reading the second object first —
call tags an unknown[].
The tags case is the nicest consequence of merging: an empty array on
its own tells you nothing, but merged with a populated one somewhere else in the sample it resolves properly. So a field only
falls back to unknown[] if it was empty everywhere.
Details that make the output actually compile
Generating plausible-looking TypeScript is easy; generating TypeScript that compiles takes a handful of specific decisions:
array root interface RootItem + type Root = RootItem[]
"my-key" quoted — not a valid identifier
default left unquoted — legal as a property name
union in array (string | null)[] — parenthesised
The first is the one that bites: interface Root {…}[] is not
valid TypeScript, so an array root has to become a named element interface plus an alias. The third is worth noting in the other
direction — reserved words are allowed as property names, so quoting
default is unnecessary noise rather than a correctness fix.
Interface names come from the key, and are singularised only when naming an array's element type. That distinction matters: a
field called address must not become
Addres. Words ending in -us, -ss and -is are left alone too, so
status and analysis
survive intact.
Identical shapes share one interface
If two different keys hold structurally identical objects, they get one interface rather than two copies. That keeps the output short and usually reflects reality, since the same shape appearing twice is normally the same concept.
The trade-off is that the shared interface takes its name from whichever key was seen first, which can read oddly — two arrays
of { n: number } called
categories and boxes
will both use Category. Rename it if the meaning differs; switch to
inline mode if you would rather have no shared names at all.
What JSON cannot tell you
These limits are worth being explicit about, because they are the reason generated types need reviewing rather than trusting.
JSON has one numeric type, so an integer ID and a price are both number.
A date is a string and indistinguishable from any other string. An enumerated field looks like a plain
string rather than a union of its allowed values. And a
null tells you a field can be null without saying what it holds when
it is not.
Most importantly, the types describe your sample and nothing more. A field the API sometimes omits will be marked required if your sample always had it. Paste a generous, varied sample, then read the output as a draft to check — which is exactly what the notes beneath the widget are prompting you to do.
FAQ
What makes this different from other JSON-to-TypeScript tools?
It reads every element of an array rather than just the first one. Given [{"a":1},{"a":1,"b":2}], a first-element-only tool either omits b or declares it required; this one emits b?: number, because b was present in one object out of two. On real API responses, where optional fields are exactly what you need to know about, that is the difference between types that compile and types that lie.
Why is my field marked optional when the API always sends it?
Because it was missing from at least one object in the sample you pasted. The tool can only describe the JSON in front of it — it has no access to your API's schema. That cuts both ways: a field your sample never omits will be marked required even if the API sometimes omits it. Treat the output as a strong first draft to check against the real contract, not as the contract itself.
Why did an empty array become unknown[]?
Because an array with no elements contains no information about its element type, and guessing would be worse than admitting it. Interestingly, if the same field is a populated array elsewhere in your sample the two get merged, so tags: ["a"] in one object and tags: [] in another yields string[] rather than unknown[]. Only a field that is empty everywhere stays unknown.
Why can a root array not be an interface?
Because TypeScript has no syntax for it — interface Root {...}[] is not valid, which is a mistake some generators make. When the root of your JSON is an array, this emits an interface for the element type and a type alias for the array itself: interface RootItem {...} followed by type Root = RootItem[]. That compiles and is what you want to import.
Should I pick interfaces or type aliases?
Either works for describing JSON. Interfaces are the conventional choice for object shapes and can be extended and merged; type aliases are required for anything that is not an object, which is why an array root always produces one regardless of the setting. If your codebase has a convention, follow it — the toggle exists so you can.
Why are all the numbers just "number"?
Because JSON does not distinguish integers from floats — 1 and 1.0 are the same token — and TypeScript has a single number type anyway. Similarly a date arrives as a string and there is no way to tell it from any other string, so it is typed as string. Those are limits of the format rather than of this tool.
Is my JSON uploaded?
No, it is analysed in the page. Worth knowing, because the JSON people paste into these tools is usually a real API response complete with real customer data.
How we compare
| Feature | Online Tool Store | Simple online generators | quicktype or a schema tool |
|---|---|---|---|
| JSON never leaves your device | ✓ | ✗ | Locally, yes |
| Merges every array element | ✓ | ✗ | ✓ |
| Valid output for an array root | ✓ | Often broken | ✓ |
| Says what it could not infer | ✓ | ✗ | ✗ |
| Nothing to install | ✓ | ✓ | ✗ |
| Other target languages | ✗ | ✗ | ✓ |
| Runtime validators and enum detection | ✗ | ✗ | ✓ |
Right for pasting a response and getting correct types in a few seconds, without sending customer data to a service. If you need types generated in your build, runtime validators, enum detection or other languages, a dedicated code generator is the better investment — and if the API publishes an OpenAPI or JSON Schema document, generate from that instead of from a sample.