How to Generate TypeScript From JSON
Paste representative JSON and choose a root interface name. The generator infers a useful first draft of the data shape. Review the output against the API’s documented contract before adding it to a project.
How JSON Values Map to TypeScript Types
Strings, booleans and numbers map to their TypeScript counterparts. Objects become structured declarations and arrays need an element type. Runtime JSON parsing and TypeScript compile-time typing are separate concerns.
Nested Interfaces and Arrays
Nested objects can produce named interfaces so complex responses remain readable. Inspect arrays with different element shapes and resolve naming collisions. A short sample may omit variants seen in production.
Optional, Nullable and Repeated Fields
Null in a sample does not reveal the intended non-null type. A missing field can indicate an optional property, but a single record cannot prove optionality. Review unions and repeated shapes explicitly.
Using Generated Types With APIs
Generated interfaces help editors and static checks, but they do not validate a network response. Pair them with runtime validation where untrusted data enters your application and keep generated types aligned with API changes.
More examples and technical details
How Types Are Inferred From a Sample
Generating TypeScript from JSON is structural inference. The generator walks the value, and at every node it asks what shape this is: an object becomes an interface with one member per key, an array becomes an element type followed by square brackets, and a primitive becomes string, number, or boolean.
For an array of objects, the generator does not stop at the first element. It merges every element to build one type that describes all of them, which is what makes optional properties and unions possible. Nested objects become their own named interfaces rather than inline literals, because named types produce far more readable errors and can be imported individually.
Side by Side Example
A paginated API response and the interfaces it produces.
JSON response
Generated TypeScript
Two keys picked up a question mark because they were present in one result and absent in the other. And next_cursor was typed as null, which is almost certainly wrong: see the caveat section.
Interface or Type Alias
| Situation | Prefer | Reason |
|---|---|---|
| Describing an object shape | interface | Idiomatic, and error messages name the interface |
| A public API surface others extend | interface | Declaration merging lets consumers augment it |
| A union of several shapes | type | Interfaces cannot express a union |
| A primitive or tuple alias | type | Interfaces only describe object shapes |
| A mapped or conditional type | type | Only type aliases support these operators |
| Anything recursive | Either | Both handle self reference correctly |
The practical difference is smaller than the debate around it suggests. Object shapes as interfaces and everything else as type aliases is a rule that never causes friction.
Optional Properties, Unions, and Null
The interesting inference happens when records disagree with each other.
- A key present in some records and missing from others is marked optional with a question mark, which types it as its inferred type or undefined.
- A key that holds different types across records produces a union, so a field seen as both a number and a string becomes string | number.
- A key that is sometimes null and sometimes a value becomes a nullable union such as string | null. This is different from optional, and the difference is real: optional means the key may be absent, nullable means the key is present and empty.
- An array with mixed element types produces an array of a union, written as (string | number)[]. An empty array in the sample gives the generator nothing to work with and produces unknown[] or any[].
- An object with no keys at all becomes Record<string, unknown>, since there is nothing to describe.
Under strictNullChecks, which every project should have on, null and undefined are not assignable to other types. That is what makes the optional versus nullable distinction enforceable instead of decorative. A field typed string | null forces you to handle the empty case at the point of use, which is the entire value of the exercise.
The Caveat That Matters Most
Types generated from a sample describe that sample. They do not describe the API contract, and the difference between those two things is where the bugs live.
| What the sample showed | What was generated | What is actually true |
|---|---|---|
| next_cursor was null | next_cursor: null | string | null once there is a next page |
| Every user had a locale | locale: string | Optional, absent for new accounts |
| status was always active | status: string | A closed union: "active" | "suspended" | "closed" |
| tags was an empty array | tags: unknown[] | string[] |
| No error object appeared | Not present at all | Returned on every failure path |
| id looked numeric | id: number | Sent as a string by the API in some regions |
Treat the generated file as a first draft that saves you the typing, then read the API documentation and correct it. Pay particular attention to fields that were null or empty in your sample, enumerable status fields that deserve a literal union, and every error shape, because error responses are almost never in the happy path sample you copied.
Types Vanish at Runtime
TypeScript types are erased during compilation. Nothing checks that the JSON arriving over the network actually matches the interface you wrote, and a response that has changed shape will flow straight into code that assumes otherwise.
A type assertion is a promise, not a check
Runtime validation libraries such as Zod, Valibot, io-ts, and ArkType let you declare the shape once and get both a runtime check and a static type from it. For anything crossing a trust boundary, which means network responses, form input, webhook payloads, and files on disk, that is worth the small amount of extra code.
Types are one way to pin down a payload's shape. Look at the full converter collection for JSON Schema when you need runtime validation rather than compile time types, JSON to GraphQL when the same shape needs an SDL definition, and JSON to SQL when it needs a table to live in.
Standards and references
Frequently asked questions
How do I generate a TypeScript interface from JSON?
Paste sample JSON, set the root name and copy the generated interface declarations.
How are arrays typed?
Array types are inferred from available sample values; review heterogeneous or empty arrays.
How are null values handled?
Null needs an explicit type decision or union because it does not reveal future values.
Can nested objects become separate interfaces?
Yes. Nested object shapes can become separate named declarations.
Is sample JSON uploaded?
No. Input is processed in this browser tab and is not uploaded by the tool. Copy and download are actions you control; ordinary page analytics do not receive tool input.