Skip to content

JSON to TypeScript Interface Generator

Paste one JSON sample and get exported interfaces for the root object and every nested object, ready to drop into a .ts file.

Runs in your browserFree, no sign-upLast reviewed

Settings

Results update automatically as you type.

TypeScript

export interface Profile {
  city: string;
  followers: number;
}

export interface Root {
  id: number;
  name: string;
  active: boolean;
  tags: string[];
  profile: Profile;
}

Summary

Interfaces
2
Input size
145 chars

Quick answers

How do I convert JSON to a TypeScript interface?
Write an interface with one property per JSON key and give each the type of its value: string, number, boolean, another interface for a nested object, or an element type plus [] for an array. Pasting the JSON above does this for the whole tree and names the nested interfaces for you.
What TypeScript type does a JSON null become?
The literal type null, because one sample cannot show what the field holds when it has a value. With Mark null values optional ticked the line is written as nickname?: null; unticked it is nickname: null. Change it by hand to the real type, for example nickname: string | null.
How are nested JSON objects typed?
Each nested object gets its own exported interface, named after the key that holds it in PascalCase, and the parent refers to it by name. {"profile":{"city":"London"}} gives interface Profile { city: string; } and a parent line profile: Profile;. Nested interfaces are printed before the root.
How is a JSON array typed in TypeScript?
By its elements. ["math","computing"] becomes string[], an array of objects becomes an interface plus [], and a nested array such as [[1,2],[3,4]] becomes number[][]. Mixed elements give a union in parentheses, for example (number | string | boolean | null)[]. An empty array becomes unknown[].
What happens when the JSON root is an array?
You get an interface for the elements and a type alias for the root. [{"id":1,"name":"Ada"},{"id":2,"name":"Grace"}] with the root name Users produces export interface User { id: number; name: string; } and export type Users = User[];.
Should I use interface or type for JSON data?
Either works for plain object shapes. Interfaces can be extended and merged; type aliases are needed for unions and for non-object roots. This tool writes interfaces for objects and a type alias only when the root is an array or a primitive.

What JSON to TypeScript does

This generator reads a JSON sample and writes one exported TypeScript interface per distinct object shape. Strings, numbers and booleans map to string, number and boolean; a nested object becomes its own interface named after its key in PascalCase; an array becomes the element type followed by []. The default sample, an object with id, name, active, tags and a nested profile, produces a Profile interface and a Root interface whose profile field is typed as Profile. Types come from the single sample you paste, so the output is a first draft: a field that is null in the sample, or absent from it, has to be corrected by hand.

JSON to TypeScript type mapping used by this generator

JSON valueGenerated TypeScriptNotes
"Ada Lovelace"stringDates and UUIDs are strings too: "2026-10-08T09:30:00Z" gives string
42, 9.99, 1e-3numberNo distinction between integers and decimals
true or falsebooleanNever narrowed to the literal true or false
nullnullWritten as key?: null when Mark null values optional is ticked
{"city":"London"}A named interfaceNamed after the key in PascalCase
{} (empty object)Interface with [key: string]: unknown;An index signature, since there are no keys to list
["a","b"]string[]One element type gives a plain array type
[1,"two",true,null](number | string | boolean | null)[]Union of every element type seen, in order of first appearance
[[1,2],[3,4]]number[][]Nested arrays nest the brackets
[] (empty array)unknown[]Nothing to inspect; replace with the real element type
[{"id":1},{"id":2}]Item[] style: an interface plus []Interface name is the key made singular

Worked examples: JSON in, interfaces out

Results are the generator's exact output, shown on one line with interfaces separated by a slash.

The default sample

{"id":42,"name":"Ada Lovelace","active":true,"tags":["math","computing"],"profile":{"city":"London","followers":1024}}

export interface Profile { city: string; followers: number; } / export interface Root { id: number; name: string; active: boolean; tags: string[]; profile: Profile; }

Summary: Interfaces 2.

A paginated API response

{"data":[{"id":1,"title":"Post","author":{"id":9,"name":"Ada"},"tags":["a"]}],"meta":{"page":1,"total":40},"error":null} with Root interface name PostsResponse

export interface Author { id: number; name: string; } / export interface Data { id: number; title: string; author: Author; tags: string[]; } / export interface Meta { page: number; total: number; } / export interface PostsResponse { data: Data[]; meta: Meta; error?: null; }

Four interfaces. Rename Data to Post and replace error?: null with the real error type.

Two objects with the same shape

{"billing":{"street":"1 Main St","city":"Leeds"},"shipping":{"street":"2 Side St","city":"York"}} with Root interface name Order

export interface Billing { street: string; city: string; } / export interface Order { billing: Billing; shipping: Billing; }

One interface is reused and takes its name from the first key that had that shape. Renaming it Address reads better.

Array elements with different fields

{"users":[{"id":1,"name":"Ada"},{"id":2,"name":"Grace","email":"g@example.com"}]} with Root interface name UserList

export interface User { id: number; name: string; } / export interface User2 { id: number; name: string; email: string; } / export interface UserList { users: (User | User2)[]; }

Shapes are not merged. Collapse these by hand into one User with email?: string.

Keys that are not valid identifiers

{"first-name":"Ada","2fa":true,"user id":7,"$ref":"x","_private":1} with Root interface name api response

export interface ApiResponse { "first-name": string; "2fa": boolean; "user id": number; $ref: string; _private: number; }

Keys with hyphens, spaces or a leading digit are quoted; $ and _ are legal and left bare.

A root that is not an object

[1,2,3] with Root interface name Scores

export type Scores = number[];

Summary: Interfaces 0.

What one JSON sample cannot tell the generator

Every type on this page is inferred from the values in the sample you paste and from nothing else. That has three consequences worth knowing before you commit the output.

A field that is null in the sample is typed null. The generator has no way to know it is a string on other responses, so nickname comes out as nickname?: null and you write string | null yourself. Pasting a sample where the field has a value gives the right base type, and you then add | null.

A field that is absent from the sample does not appear at all, and a field that is present is never marked optional unless its value is null. The question mark this tool writes means the value was null here, not that the key is sometimes missing.

Objects inside an array are compared, not merged. If every element has the same keys, in the same order, with the same types you get one interface. If one element has an extra key, lists its keys in a different order, or has a null where another has a string, you get two interfaces and a union such as (Row | Row2)[]. The fix is to merge them by hand into one interface with optional or nullable fields, or to paste a sample whose elements are all complete.

How interfaces are named

SituationExample key or nameInterface name
Root objectRoot interface name left as RootRoot
Root name with spaces or punctuationapi responseApiResponse
Root name starting with a digit2faItem2fa
Root name left blank(empty)Root
Nested objectprofileProfile
Objects in an array, plural ending in sitems, usersItem, User
Plural ending in iescategoriesCategory
Plural ending in xes, ses, ches, shes or zesboxes, addressesBox, Address
Words the suffix rule gets wrongstatus, casesStatu, Cas (rename them)
A word with no plural formdataData
Name already taken by a different shapea second profile object with other fieldsProfile2
Nested key that matches the root nameroot inside RootThe nested object takes Root and the root becomes Root2

Names are derived mechanically from keys. The singular rule is a simple suffix rule, so check names that come from array keys.

Checklist before you use the generated types

  1. Search the output for null and replace each one with the real type, such as string | null.
  2. Search for unknown and fill in element types for arrays that were empty in the sample.
  3. Add ? to any field the API omits in some responses.
  4. Merge numbered duplicates such as User and User2 into one interface.
  5. Narrow fixed sets of strings to literal unions, for example status: "draft" | "published".
  6. Rename interfaces that inherited an unhelpful key name, such as Data or Statu.
  7. Remember that interfaces are erased at compile time. They describe the response but do not validate it; use a runtime schema library if you need to reject bad data.

Using JSON to TypeScript

  1. 1

    Paste a representative response into JSON. It has to be strict JSON: double-quoted keys, no trailing commas, no comments.

  2. 2

    Set Root interface name to something meaningful such as OrderResponse. It is converted to PascalCase, so api response becomes ApiResponse.

  3. 3

    Leave Mark null values optional ticked to write a null field as name?: null, or untick it to write name: null.

  4. 4

    Copy the TypeScript or download it as types.ts. The Summary shows how many interfaces were generated.

  5. 5

    Replace every null and unknown[] with the real type, and add ? to fields your API can leave out.

Questions people ask

Is my JSON sent anywhere?
No. Parsing and type generation run as JavaScript in your browser, and the sample you paste is not transmitted. You can use a real production response without it leaving your machine.
Why did I get User and User2 for one array?
Two elements of the array had different fields, the same fields in a different order, or different field types, and the generator emits one interface per distinct shape and joins them with a union. A null in one element and a string in another is enough to cause it. Merge them manually into one interface.
Why is my field typed as null with a question mark?
Its value was null in the sample and Mark null values optional was ticked. The generator cannot see the type the field has when it is filled in. Replace null with that type, and keep or drop the ? depending on whether the key can be missing.
Does it detect dates, enums or integers?
No. A date string is typed string, a status value is typed string and every number is number, whether whole or decimal. Converting to Date objects, literal unions or branded integer types is a manual step.
Can I paste a JavaScript object literal or JSON with comments?
No. The input goes through the standard JSON parser, so unquoted keys, single-quoted strings, trailing commas and comments all stop the conversion with an error that begins "That is not valid JSON". Convert it to strict JSON first.
Can it generate type aliases, Zod schemas or readonly fields?
No. Output is always export interface for objects, with a type alias only for a non-object root. There are no options for type aliases, readonly modifiers, removing export, or runtime schemas such as Zod.
Why do large ID numbers need care?
JSON numbers are read as JavaScript numbers, which are exact only up to 9007199254740991. A larger ID is still typed number, but its last digits may already have changed when your code parses the response. APIs that use such IDs usually send them as strings.
In what order are the interfaces written?
Depth first: an interface is printed once all the objects nested inside it have been printed, so the root comes last. TypeScript does not care about declaration order for interfaces, so you can rearrange the file freely.

Looking for something else? Browse all developer tools or see every tool.