JSONToonPro
TypeScript generator

JSON to TypeScript Interface Generator

Convert JSON objects and API responses into TypeScript interfaces instantly. Nested objects become named interfaces, arrays of objects produce typed generics, and you can export type aliases or interfaces, all client-side with zero upload.

100% client sideNested interfacesDownload .ts
Root name
3 types
Input/ JSON
375 chars20 lines
Output/ TypeScript
307 chars22 lines

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

{
"page": 2,
"total": 137,
"next_cursor": null,
"results": [
{
"id": "usr_81",
"email": "kai@example.com",
"verified": true,
"profile": { "display_name": "Kai",
"locale": "en-GB" }
},
{
"id": "usr_82",
"email": "sam@example.com",
"verified": false,
"profile": { "display_name": "Sam" },
"invited_by": "usr_81"
}
]
}

Generated TypeScript

export interface Profile {
display_name: string;
locale?: string;
}
 
export interface Result {
id: string;
email: string;
verified: boolean;
profile: Profile;
invited_by?: string;
}
 
export interface ApiResponse {
page: number;
total: number;
next_cursor: null;
results: Result[];
}

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

SituationPreferReason
Describing an object shapeinterfaceIdiomatic, and error messages name the interface
A public API surface others extendinterfaceDeclaration merging lets consumers augment it
A union of several shapestypeInterfaces cannot express a union
A primitive or tuple aliastypeInterfaces only describe object shapes
A mapped or conditional typetypeOnly type aliases support these operators
Anything recursiveEitherBoth 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 showedWhat was generatedWhat is actually true
next_cursor was nullnext_cursor: nullstring | null once there is a next page
Every user had a localelocale: stringOptional, absent for new accounts
status was always activestatus: stringA closed union: "active" | "suspended" | "closed"
tags was an empty arraytags: unknown[]string[]
No error object appearedNot present at allReturned on every failure path
id looked numericid: numberSent 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

// No validation happens here at all
const data = (await res.json()) as ApiResponse;
 
// A schema validates and infers the type from one source
const Result = z.object({
id: z.string(),
email: z.string().email(),
verified: z.boolean(),
});
type Result = z.infer<typeof Result>;
const data = Result.parse(await res.json());

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.

How to generate TypeScript interfaces from JSON

Three steps
01

Paste or upload JSON

Add any JSON object or array to the input panel. Use the sample button to load a realistic API response with nested objects and arrays.

02

Tune generation options

Choose interface or type alias, toggle key sorting, and set the root interface name to match your codebase convention.

03

Copy or download .ts file

Interfaces update live as you type. Copy the output or download a ready-to-import .ts file.

Built for TypeScript developers

Stop writing interfaces by hand. Paste a JSON API response and get typed interfaces in seconds, ready for your TypeScript project.

100% client side

Your JSON never leaves the browser. No upload, no server, no logs.

Live generation

TypeScript interfaces update as you type with clear error messages for invalid JSON.

Nested interfaces

Objects within objects each get their own named interface in PascalCase.

Array generics

Arrays of objects produce a typed item interface and a typed array alias.

Type aliases

Toggle between interface and type alias declarations to match your project style.

Download .ts file

Export all generated interfaces as a ready-to-import TypeScript file.

Frequently asked questions

7 answers
Paste your JSON into the input panel. TypeScript interfaces are generated instantly. Nested objects become named interfaces, arrays of objects produce typed generics, and primitive values map to string, number, or boolean.

TypeScript Interface Generator from JSON

This tool infers TypeScript types from JSON values: strings become string, numbers become number, booleans become boolean, and null becomes null. Objects generate named interfaces derived from the property key in PascalCase. Arrays of objects merge all item shapes and produce a typed item interface. You can switch to type aliases, sort keys alphabetically, and rename the root interface to match your project conventions.

More JSON Tools