JSON to TypeScript Generator

Generate TypeScript interfaces from any JSON. Handles nested objects, arrays, union types, and optional properties. Free, runs in your browser.

  • Runs in your browser
  • Your data never leaves your browser
  • Free · No Sign-Up
Enter a valid TypeScript name. A blank field uses RootObject. Click Generate TypeScript to apply a change.
Add ? to every generated object property. Even when this is off, fields missing from some array items are optional. Click Generate TypeScript to apply the option.
Generate object declarations with type instead of interface. Primitive roots already use type. Click Generate TypeScript to apply the option.
Generate now with the current JSON, root name and options. Use this after changing the name or options. Ctrl/⌘+Enter also generates.
Replace the JSON input with a sample containing nested objects, arrays and null. Generate immediately with the current root name and options.
Clear JSON, output and status. Keep the root name and both options. Ctrl/⌘+L, with focus in the tool, also clears the root name.
Enter valid JSON without comments or trailing commas. Generation runs 300 ms after typing stops; invalid JSON clears the output and shows an error.
TypeScript Output
Copy the complete displayed TypeScript output. Empty output is not copied.

Enter JSON or load the example to generate TypeScript.

Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.

Type Inference Rules

  • null → optional null (field?: null)
  • string → string
  • number → number
  • boolean → boolean
  • empty array → unknown[]
  • array of objects → merged interface + ItemName[]
  • array of mixed primitives → union, e.g. (string | number)[]
  • nested object → separate named interface

Example

Given this JSON:

{"user": {"name": "Alice", "age": 30}, "tags": ["admin", "user"]}

The tool generates:

interface RootObject {
  user: User;
  tags: string[];
}

interface User {
  name: string;
  age: number;
}

Example: An API Response

{
  "user": {
    "id": 42,
    "name": "Alice",
    "email": "alice@example.com",
    "roles": ["admin", "editor"],
    "preferences": { "theme": "dark", "notifications": true },
    "lastLogin": null
  },
  "pagination": { "page": 1, "perPage": 20, "total": 143 }
}

generates

interface RootObject {
  user: User;
  pagination: Pagination;
}

interface User {
  id: number;
  name: string;
  email: string;
  roles: string[];
  preferences: Preferences;
  lastLogin?: null;
}

interface Preferences {
  theme: string;
  notifications: boolean;
}

interface Pagination {
  page: number;
  perPage: number;
  total: number;
}

The root interface comes first, then each nested interface in the order it is found. A null value is typed null and marked optional, because one sample cannot show what the field holds when it is set. After checking the API docs, change it to something like lastLogin: string | null.

Example: Arrays With Different Shapes

{
  "items": [
    { "id": 1, "name": "a", "tags": [] },
    { "id": 2, "price": 9.5, "tags": ["x", 1] }
  ],
  "messages": [
    { "type": "text", "content": "hi" },
    { "type": "image", "url": "/a.png" }
  ]
}

generates

interface RootObject {
  items: ItemsItem[];
  messages: MessagesItem[];
}

interface ItemsItem {
  id: number;
  name?: string;
  tags: (unknown[] | (string | number)[]);
  price?: number;
}

interface MessagesItem {
  type: string;
  content?: string;
  url?: string;
}

All objects in an array are merged: a key that is missing from some elements gets ?. The messages array is really two shapes keyed by type. The merged interface accepts invalid mixes such as a text message with a url, so rewrite it as a discriminated union (TypeScript Handbook: Narrowing):

type Message =
  | { type: "text"; content: string }
  | { type: "image"; url: string };

Example: Two Objects Under the Same Key

{
  "order": { "id": "A-1001", "meta": { "createdBy": "alice", "source": "web" } },
  "payment": { "status": "paid", "meta": { "provider": "stripe", "fee": 0.3 } }
}

generates

interface RootObject {
  order: Order;
  payment: Payment;
}

interface Order {
  id: string;
  meta: Meta;
}

interface Meta {
  createdBy: string;
  source: string;
}

interface Payment {
  status: string;
  meta: PaymentMeta;
}

interface PaymentMeta {
  provider: string;
  fee: number;
}

order.meta takes the name Meta. payment.meta has different fields, so it gets its own interface, named with its parent in front: PaymentMeta. If it had the same fields as order.meta, both would share Meta. Merging the two into one Meta with every field optional would accept objects that neither API returns, so each shape keeps its own type. If the name is still taken, a number is added (Meta2). The root keeps the name you chose even when a nested key has the same name.

What to Refine by Hand

  • null fields: replace ?: null with the real type plus | null.
  • Empty arrays: [] gives unknown[]; supply the element type.
  • Dates: ISO 8601 strings and Unix timestamps stay string and number.
  • Keys that are not valid identifiers (user-name, 2fa) are written in quotes, which is valid TypeScript.
  • Generated names such as PaymentMeta or Meta2 come from the JSON keys; rename them to match your domain.
  • A key that gives no valid type name (2fa, an empty key) gets a T prefix for its interface (T2fa); the property itself keeps the original key in quotes.

Limits

  • The input must be valid JSON (no comments or trailing commas); the error message gives the position.
  • Types come only from the sample you paste. A field that is a string in your sample but sometimes a number in production stays string.
  • With Use type instead of interface, each declaration is written as type Name = { ... }; the status line still says “interfaces”.

FAQ

What does this tool generate?

It generates TypeScript interface (or type alias) declarations from a JSON object. Each nested object becomes its own named interface. Array items with mixed types produce union types.

How are nested objects handled?

Each nested object field is extracted into a separate interface named after the field key in PascalCase. For example, a field named 'address' becomes an 'Address' interface, and the parent interface references it by name.

What happens with arrays of objects?

If an array contains objects, all objects are merged to form a single item interface. Fields present in some but not all objects are marked as optional.

What is the 'Make properties optional' option?

When checked, every property in every generated interface gets a '?' suffix, making it optional. Useful when you expect partial data or want a loose type definition.

What is the difference between interface and type?

Both declare object shapes in TypeScript. 'interface' supports declaration merging and is idiomatic for object types. 'type' is more flexible and can also represent primitives, unions, and tuples. Toggle 'Use type instead of interface' to switch.

Is my data sent to a server?

No. The entire type inference engine runs in your browser. No data leaves your machine.