How to turn JSON into a TypeScript interface
Get types from a real API answer in seconds, and know what the generated types cannot tell you.
You have an API answer and no types for it. Writing the interface by hand is slow and you will get a key wrong. A tool can read the answer and write the types for you in a second.
This page shows how, and — more useful — says what the generated types get wrong, so you know what to fix by hand.
The quick way
Open the answer in the viewer and choose Tools → To a TypeScript interface. For this input:
{
"id": 42,
"name": "Ada",
"active": true,
"tags": ["admin", "staff"],
"profile": { "city": "London", "zip": "N1" },
"deleted": null
}
you get:
export interface Profile {
city: string
zip: string
}
export interface Root {
id: number
name: string
active: boolean
tags: string[]
profile: Profile
deleted: null
}
Each nested object becomes its own interface, named from the key it sits
under. A list of objects gives the item type a singular name, so users
produces a User interface.
Optional keys
If the answer is a list, the tool reads every record and marks a key optional when some records do not have it:
{"items": [{"id": 1, "note": "x"}, {"id": 2}]}
export interface Item {
id: number
note?: string
}
This is the main reason to generate the types from real data and not from one example record. One record cannot tell you which keys are optional.
What the types cannot tell you
A generated type describes the sample you gave it, not the API. These are the four things to fix by hand.
1. null means "unknown", not "always null"
deleted: null
That is almost never right. The field is probably string | null or
Date | null, and your sample happened to have no value. Look at every
null in the output and decide what the other case is.
2. An empty list tells you nothing
{"tags": []}
gives tags: unknown[]. Find a record that has tags, or write the type
yourself.
3. Text is sometimes more than text
status: string
date: string
The API probably allows only "active" | "suspended" | "closed". A union type
catches a mistake that string lets through:
status: 'active' | 'suspended' | 'closed'
date: string // ISO 8601, for example 2026-11-04T09:30:00Z
4. A number may not survive as a number
If an identifier has more than 15 digits, TypeScript's number cannot hold it
exactly, and JSON.parse has already changed it before your code runs. Type
it as string and make the server send it as text. See
big numbers in JSON.
Validate, do not only type
This is the point most people miss.
const user = await res.json() as User
That cast tells the compiler to trust you. It checks nothing at run time. If the API changes, your program carries wrong data until it fails somewhere far away from the cause.
A schema library checks the shape when the data arrives:
import { z } from 'zod'
const User = z.object({
id: z.number(),
name: z.string(),
active: z.boolean(),
tags: z.array(z.string()),
profile: z.object({ city: z.string(), zip: z.string() }),
deleted: z.string().nullable(),
})
type User = z.infer<typeof User>
const user = User.parse(await res.json()) // fails here, with a clear message
The type comes from the schema, so there is one description, not two that drift apart.
If you prefer JSON Schema, write the schema and check the answer against it. This viewer has a Schema action that does exactly that, so you can test the schema against a real answer before you put it in your code. See JSON Schema.
Other ways to get types
| Way | Good for |
|---|---|
| This viewer | One answer, right now, with no upload |
quicktype |
A whole folder of samples, many languages |
| OpenAPI generator | An API that publishes a specification |
zod and friends |
Types and a check at run time |
If the API publishes an OpenAPI document, use it. Types generated from the specification describe what the API promises. Types generated from one answer describe what it happened to send that day.
A good routine
- Fetch a real answer and save it.
- Generate the types from it.
- Read the output and fix the four things above.
- Turn the fixed type into a run-time schema.
- Keep the saved answer in your tests, and compare it with a fresh one when the API changes.
Try it on your own file. The tool runs in your browser. Nothing is uploaded, and nothing is stored on a server.
Open the JSON ViewerRead next
JSON Schema — check your data before it breaks something
Write a schema that catches bad data, with the keywords that matter and the traps that waste a day.
Why big numbers break in JSON, and what to do
An identifier of 19 digits goes into a parser and comes out changed. Here is why, and the three ways to stop it.