Why generate types from JSON?
TypeScript’s value comes from knowing the shape of your data. The moment a value is typed any, the compiler stops helping you — no autocomplete, no checks, no refactoring safety. Yet the data you most want typed often arrives as JSON: API responses, configuration files, webhook payloads, and exported records. Writing interfaces for those by hand is slow, and it drifts out of sync the moment the data changes.
Generating types from a real JSON sample flips the work around. Instead of describing the data from memory, you let a representative example define the shape, and you get compile-ready interfaces in seconds. The JSON to TypeScript tool does exactly this in your browser. This guide explains how the inference works so you can trust the output and know where to refine it.
The core idea: walk the value, map each node
JSON has a small, well-defined set of value types: objects, arrays, strings, numbers, booleans, and null. TypeScript has direct equivalents for the primitives, plus interfaces for object shapes and T[] for arrays. Type inference is really just a recursive walk that maps each JSON node to its TypeScript counterpart.
The rules are intuitive:
- A string becomes
string, a number becomesnumber, a boolean becomesboolean. - null becomes the
nulltype. - An object becomes a named interface, with each key turned into a property.
- An array becomes an element type followed by
[].
Because the walk is recursive, nesting falls out naturally: an object inside an object produces a nested interface, an array of objects produces an array of a generated interface, and so on to any depth.
Naming nested interfaces
A flat object is easy — one interface with a few properties. Real data is rarely flat, so the interesting question is what to call the interfaces for nested objects. The convention the tool follows is to name each nested interface after the key that holds it, converted to PascalCase. A profile object becomes a Profile interface; an address becomes Address.
When the same name would be generated twice, the tool appends a number to keep every interface name unique, so the output always compiles. You can rename anything afterwards — generated names are a starting point, and a quick find-and-replace in your editor tidies them to your taste. If you want to inspect the structure before generating types, running the sample through a JSON formatter first makes the nesting easy to see.
Arrays: the part that needs judgement
Arrays are where naive generators get it wrong. There are two cases worth understanding.
Arrays of primitives are simple: ["a", "b"] is string[], [1, 2, 3] is number[]. If the elements are of mixed types — say [1, "two", true] — the tool produces a union element type, (number | string | boolean)[], so every element is covered.
Arrays of objects require merging. Consider this:
{
"users": [
{ "id": 1, "name": "Asha" },
{ "id": 2, "name": "Ravi", "admin": true }
]
}
A weak generator infers the element type from the first object only and misses admin. The JSON to TypeScript tool instead merges every object in the array: id and name appear in both, so they’re required, while admin appears in only one, so it becomes optional (admin?: boolean). The result is a single User interface that genuinely describes every element.
The optionality edge case
Inference can only see what’s in your sample, and this matters most for optionality. If a field is sometimes absent, your sample must include an object that omits it for the tool to mark it optional. Likewise, a field that’s sometimes null will only be typed as null if a null appears in the sample.
The practical takeaway: use a representative sample, not the happy-path minimum. If an API sometimes omits a field or returns null, include an example that shows it. When you can’t, generate the types and then add the ? or | null yourself — the generated output is a fast first draft, not a contract.
Empty arrays and unknowable values
What type is []? There are no elements to infer from, so the honest answer is “could be anything.” The tool emits any[] by default, or unknown[] if you enable the safer option. unknown forces you to narrow the type before using elements, which is stricter and often what you want in a well-typed codebase. Either way, an empty array is a signal to supply a better sample or annotate the element type manually.
Interface vs type, and other output choices
The generator lets you tune the output to match your project:
- Interface or type alias. Emit
interface User { ... }ortype User = { ... }. Interfaces support declaration merging; type aliases compose more flexibly with unions. For plain data models, both are fine. - Export keyword. Toggle
exporton so the types are importable across modules, or off for local use. - Semicolons. Match your formatter’s style so the output drops in without reformatting.
unknownoverany. Preferunknownfor stricter handling of empty arrays and unknowable values.
These are cosmetic in the sense that they don’t change the inferred shapes — they make the output fit your conventions so you paste it in without friction.
A realistic workflow
Here’s how this fits a normal development loop. You hit an API endpoint and copy a real response. You paste it into the JSON to TypeScript tool, set a meaningful root name like OrderResponse, and copy the generated interfaces into your types file. You then refine: mark fields optional that the sample didn’t reveal, rename a nested interface or two, and add a | null where the API can return null. Total time: under a minute, versus the ten or fifteen it takes to write nested interfaces by hand — and with far less chance of a typo in a field name.
If your data also needs to travel between formats, the same structural thinking applies when you convert JSON to YAML for config files, or when you compare two payloads with a structured diff. Our guide to JSON diffing covers how the same tree-walking model powers comparison as well as code generation.
The takeaway
Generating TypeScript from JSON is a recursive mapping: primitives map directly, objects become named interfaces, and arrays become element-typed arrays with objects merged so optional keys are detected. The one thing inference can’t do is read your mind about optionality and nullability — so feed it a representative sample and refine the result. Done that way, JSON to TypeScript turns one of the more tedious parts of typed development into a paste-and-go step.