↑↓ to move ↵ to open Esc to close Browse all tools

JSON to TypeScript

Paste JSON, like an API response, and get TypeScript interfaces or type aliases for it. Nested objects get their own types and arrays of objects are merged, with optional keys where items differ.

Paste a JSON object or array, or drop a .json file here. The more sample items an array has, the better the types.
Declare as
TypeScript
The TypeScript types appear here.

Your JSON is converted in your browser. Nothing is uploaded.

Get TypeScript types from a JSON sample

When you call an API from TypeScript, you need a type for what comes back. Writing it by hand from a long response is slow and easy to get wrong. This JSON to TypeScript converter reads a sample, such as a response you copied from the network tab or the docs, and writes the interfaces or type aliases for it. Nested objects get their own named types, arrays of objects are merged into one type with optional keys where the items differ, and mixed values become union types. All of it runs in your browser.

To check or tidy the JSON first, use the JSON formatter. If your data starts as YAML, convert it with YAML to JSON, and to open a JSON array in a spreadsheet, use JSON to CSV.

How JSON values become types

JSON, as RFC 8259 defines it, has strings, numbers, true and false, null, objects and arrays. Each one maps to a TypeScript type from the handbook’s everyday types:

In the JSONIn TypeScript
"text"string
42, 9.5, 1e3number
true, falseboolean
nullnull
{ "city": "Paris" }A named interface, like Address
[1, 2, 3]number[]
[1, "two"](string | number)[]
[]unknown[]
{}Record<string, unknown>

RFC 8259 says the values in an array do not have to be of the same type, so a mixed array gets a union of every type found in it. An empty array or object tells nothing about its contents, so it gets unknown rather than any. The handbook advises avoiding any, because it is not type-checked.

Arrays of objects: merged, with optional keys

API responses often hold a list of records that do not all have the same keys. The converter merges every object of an array into one type. A key that appears in every item is required. A key that is missing from some items gets a question mark, which the handbook calls an optional property. If a key holds a number in one item and a string in another, its type is the union string | number. Objects nested inside the items are merged the same way, at any depth.

The item type is named after the array’s key in the singular: users gives User, categories gives Category, addresses gives Address. A key that is not plural, like data, gives DataItem, and a root array gives RootItem.

Null values and strictNullChecks

The handbook recommends turning on strictNullChecks where it is practical; with it on, you have to test for null before you use a value. So a property that is null somewhere in your sample has to allow null. Pick how in Null values:

  • key: T | null (default): the key is always there, its value may be null. This matches APIs that send "coupon": null.
  • key?: T | null: the key may also be missing. Use it when the API sometimes leaves the key out and sometimes sends null.
  • key?: T: treats null like a missing key. Use it only when your code turns null into undefined before it uses the data.

Null inside an array, like [1, null], always stays in the element type: (number | null)[].

Names, quoted keys and repeated shapes

  • Type names are the key in PascalCase: shipping_address and shipping-address both give ShippingAddress. A name that would hide a built-in type, like Date or Map, gets the name of its parent in front, like OrderDate.
  • Different shapes under the same key get distinct names. If user.address and company.address have different keys, you get Address and CompanyAddress.
  • Identical shapes under the same key share one type, so two geo objects with the same keys give one Geo.
  • Keys that are not identifiers are quoted. MDN’s lexical grammar describes an identifier as a letter, $ or _ followed by letters, digits, $ or _, and notes that object properties may use reserved words. So class and default stay bare, while "first-name", "2fa" and "user name" are written in quotes.

interface or type, export and readonly

The TypeScript handbook says type aliases and interfaces are very similar and that in many cases you can choose freely; the key difference is that an interface can be reopened to add properties. Its rule of thumb is to use interface until you need a feature of type. A root that is an array or a single value is always a type alias, like type Root = RootItem[], because an interface can only describe an object.

Export puts export in front of every declaration so you can import the types from another file. Readonly marks every property readonly and every array readonly T[]. The handbook points out that readonly only stops a property from being reassigned during type checking and does not make its contents immutable; the readonly arrays here close that gap for lists.

Worked example

A small order from a shop API: two line items, one of which has an extra gift-wrap key, and a coupon that is null. With the root name Order and the default options:

{
  "id": 1042,
  "status": "shipped",
  "customer": { "name": "Ann Lee", "email": "ann@example.com" },
  "items": [
    { "sku": "A-100", "qty": 2, "price": 9.5 },
    { "sku": "B-220", "qty": 1, "price": 24, "gift-wrap": true }
  ],
  "coupon": null
}

The converter writes:

export interface Order {
  id: number;
  status: string;
  customer: Customer;
  items: Item[];
  coupon: null;
}

export interface Customer {
  name: string;
  email: string;
}

export interface Item {
  sku: string;
  qty: number;
  price: number;
  "gift-wrap"?: boolean;
}

The two line items were merged into Item, named after items. Only the second item has gift-wrap, so it is optional, and because its name has a hyphen, it is quoted. The coupon is null in the only sample there is, so its type is just null. Change it to string | null by hand, or paste a second order that has a coupon.

What a sample cannot tell

The types describe your sample, not the API. A key that never appears in it is missing from the type, a field that is always present in the sample may be optional in reality, and a value that happens to be a whole number is still just number. Dates in JSON are strings. For the best result, paste a response with several items and with the optional fields filled in, then compare the types with the API’s documentation.

How to use the JSON to TypeScript converter

  1. 1Paste your JSON, drop a .json file on the box or press Open file. If there is an error, read the line and column, press Jump to error and fix it.
  2. 2Type a name for the root type, like ApiResponse, and choose interface or type.
  3. 3Choose how null values are typed and whether to add export and readonly.
  4. 4Copy the TypeScript or download it as a .ts file, then check it against what your code needs.

Sources

Frequently asked questions

Should I use interface or type?

Either works for the shapes this tool makes. The TypeScript handbook says that for the most part you can choose by personal preference, and suggests interface until you need a feature of type. The main difference is that an interface can be reopened and extended with declaration merging, a type alias cannot.

Why are some properties optional?

When an array holds several objects, the tool merges them into one interface. A key that is missing from at least one of the objects gets a question mark, like gift-wrap?: boolean, because that object would not fit the type otherwise. With one of the null options, a key that is null somewhere also becomes optional.

What happens with null values?

By default a property that is null in your sample gets | null added to its type, like coupon: string | null. You can also make such properties optional as well, or optional without | null. If a key is null in every sample, its type is just null, because the JSON says nothing else about it; add a sample with a real value or edit the type.

Why is a key written in quotes?

Property names that are not valid JavaScript identifiers, like first-name, 2fa or a name with a space, have to be written as strings in TypeScript. Names made of letters, digits, $ and _ that do not start with a digit stay bare, and reserved words like class or default are allowed as property names.

Does it detect dates, integers or enums?

No. JSON has no date type, so an ISO date is a string, and TypeScript has a single number type for integers and decimals. A status like "shipped" becomes string, not a union of the values you happen to have. Narrow these by hand where your code needs it.

My JSON shows an error. What now?

The message names the line and column of the first problem and shows that line with a marker under the spot. Press Jump to error to select it in the box. Common causes are a trailing comma, single quotes, comments and unquoted keys, none of which JSON allows. The JSON formatter can repair many of them.

Is my JSON uploaded?

No. The JSON is parsed and converted in your browser, so API responses with private data stay on your device. Copy link shares only your settings.