Online Tool Store Online Tool Store

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.

Options

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

  1. Paste a JSON response — the more representative the sample, the better the types.
  2. Include several array elements if the objects vary, so optional fields are detected.
  3. Read the notes underneath: they say what could not be inferred.
  4. 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.

Explore related tools

Embed this tool

Paste this on your own site — it stays free, and every file still stays in your visitor's browser, not yours or ours.