JSON to TypeScript Converter
Paste a JSON response and get typed interfaces back, with the inference rules spelled out so you know exactly what the output is claiming.
- typescript
- tools
- api
Typing a third-party API by hand is tedious and error-prone: you miss a nested object, guess wrong about which fields are optional, and find out in production. This tool takes a JSON sample and generates the TypeScript interfaces that describe it.
Paste a response body, get types back. Nothing is uploaded — the conversion runs entirely in your browser.
What the output looks like
Given a typical API payload:
{
"id": "crs_9f2a",
"title": "Advanced TypeScript",
"price": 4900,
"isPublished": true,
"tags": ["typescript", "advanced"],
"author": { "id": "usr_11c", "name": "Ada" },
"archivedAt": null
}You get:
export interface Author {
id: string
name: string
}
export interface Course {
id: string
title: string
price: number
isPublished: boolean
tags: string[]
author: Author
archivedAt: null
}Nested objects are extracted into their own named interfaces rather than being
inlined, so you can import and reuse Author on its own.
The inference rules
Everything the tool does follows from one constraint: JSON carries values, not types. The generator reads a sample and infers the narrowest type that sample is consistent with. Knowing the rules tells you where to check the output.
Primitives
string, number, and boolean map directly. JSON has one numeric type, so
integers and floats both become number — there is no way to tell an id: 3
that must stay an integer from a rating: 3 that could be 3.5.
Arrays
The element type is the union of every member's type. An array of objects with matching shapes produces one interface; mismatched shapes produce a union, and that union is usually the signal that you are looking at a discriminated union worth naming yourself.
An empty array cannot be inferred at all and becomes unknown[].
Null
A null value in the sample means the field is nullable, but a sample can only
prove nullability — never absence. archivedAt: null above is typed null
because that is all the data supports. What you almost certainly want is:
archivedAt: string | nullPaste a second sample where the field is populated, or widen it yourself.
The three things to always fix by hand
- Optional versus nullable. A key missing from your sample is not
necessarily optional, and a key present in it is not necessarily required.
Only the API contract knows. Check the docs and add
?where it belongs. - Dates. ISO timestamps arrive as strings and are typed
string. That is correct for the wire format — decide deliberately whether your app types parse them intoDateat the boundary or keep them as strings. - String literals. A
status: "published"field becomesstring. If the API only ever returns three values, narrow it to a union and let the compiler check your switch statements.
When to reach for something stronger
Generated types describe a sample, not a contract — they will drift the moment
the API changes and nothing will tell you. For an API you own, generate types
from the schema instead: GraphQL codegen from your SDL, or Prisma's generated
client types straight from schema.prisma. Those regenerate with the source
and stay honest.
Use this tool for the other case — the undocumented endpoint, the webhook payload you captured once, the response you are typing right now so you can keep working.