Hunt
Skip to content

JSON to TypeScript

Paste a JSON response or config and get TypeScript interfaces you can copy into your code. Nested objects become named types, fields missing from some array items become optional, and mixed values become unions.

Runs in your browser

JSON

Paste JSON or press Sample

TypeScript

The TypeScript types will appear here

What this tool does

Typing an API response by hand is slow and easy to get wrong, especially when the data is deeply nested or the items of an array don't all have the same fields. This tool reads a sample of the JSON and writes the TypeScript declarations that describe it.

It runs in your browser as you type, so you can paste real responses, including ones with customer data or tokens. The result is a starting point: review it, rename types where a better name exists, and tighten anything the sample couldn't show (see below).

How to use the JSON to TypeScript

  1. Paste a JSON sample into the left panel, or press Sample to try an example.
  2. The TypeScript declarations appear on the right as you type.
  3. Set the root type name, and choose between interface and type, export and read-only properties.
  4. Copy the result, or download it as types.ts.

Examples

Generate an interface from an object

Input
{"id":7,"name":"Ada","roles":["admin","dev"],"active":true}
Output
export interface Root {
id: number;
name: string;
roles: string[];
active: boolean;
}

Fields missing from some array items become optional

Input
{"orders":[{"id":"o1","total":42,"note":"gift"},{"id":"o2","total":10}]}
Output
export interface Root {
orders: Order[];
}

export interface Order {
id: string;
total: number;
note?: string;
}

The second order has no note, so note is optional. The orders array is named in the singular: Order.

Nested objects get their own types

Input
{"user":{"id":1,"address":{"city":"London","postcode":"N1 9GU"}},"manager":null}
Output
export interface Root {
user: User;
manager: null;
}

export interface User {
id: number;
address: Address;
}

export interface Address {
city: string;
postcode: string;
}

manager is only ever null in this sample, so its type is null. In real code you would widen it, for example to User | null.

Type aliases with read-only properties

Input
{"name":"Ada","tags":["math","code"]}
Output
export type Root = {
readonly name: string;
readonly tags: string[];
};

How the types are inferred

The rules are simple and predictable, so you can tell why each type looks the way it does:

  • Objects become interfaces named after their key: a shippingAddress object becomes ShippingAddress. The top-level type is called Root unless you choose another name.
  • Arrays get one item type that covers every element. An array of objects named orders produces an Order interface.
  • Optional fields: when the objects in an array don't all have the same keys, a key missing from any of them is marked optional (note?: string).
  • Unions: if a value has different types in different places, it becomes a union, such as string | number. null is always listed last: string | null.
  • Empty values: an empty array becomes unknown[] and an empty object becomes Record<string, unknown>, because the sample says nothing about their contents.
  • Property names that aren't valid identifiers ("first name", "1st") are kept in quotes.
  • Repeated shapes: two objects with the same name and the same fields share one declaration.

A sample only shows what it contains

The types describe the JSON you paste, not every response the API can return. Before relying on them, check the places where a single sample is not enough:

  • A field that is always null in the sample is typed null. Widen it by hand, for example to string | null.
  • Dates, IDs and enums are plain string or number. If a field only ever holds "draft" | "published", write that union yourself.
  • A field missing from every item can't appear at all. Paste a sample that includes the optional parts of the response.
  • Types disappear when TypeScript compiles. To check data at runtime, use a schema library such as Zod.

Options

Root type name
The name of the top-level type. Words are joined in PascalCase, so api response becomes ApiResponse.
Declare as
interface (the default) or type. They are interchangeable for describing JSON; teams usually pick one style and stay with it.
Export
Adds export to every declaration, ready to paste into a module.
Read-only
Marks every property readonly, useful for data you treat as immutable, such as API responses.

Limitations

  • Types are inferred from one sample; they can't include fields, values or variations the sample doesn't contain.
  • All numbers are number; strings that hold dates, IDs or enum values are string.
  • Strict JSON only. Comments, trailing commas and single quotes are reported as errors.
  • Inputs up to 10 MB are supported. Generation runs in your browser.

Frequently asked questions

Is my JSON sent to a server?

No. The types are generated entirely in your browser. Nothing you paste is uploaded, stored or logged, so you can use real API responses.

Should I use interface or type?

For describing JSON objects they behave the same. Interfaces can be extended and merged, and their names show in error messages; type aliases can also describe unions and primitives. Pick the style your codebase already uses.

Why is a field marked optional?

Because it is missing from at least one item of an array in your sample. If the field is always present in real data, remove the ?; if it can also be null, add | null.

Why do I get unknown[] for an array?

The array is empty in your sample, so there is nothing to infer its items from. Paste a sample where the array has at least one item, or replace unknown with the right type.

Do these types check data at runtime?

No. TypeScript types are removed when your code is compiled, so they can't catch an API that returns something unexpected. For runtime checks, validate the data with a schema library such as Zod, and derive the types from the schema.