Skip to content

Runs entirely in your browser. Nothing you paste leaves this page.

Free / No sign-up

JSON to TypeScript converter: interfaces from JSON.

Paste a JSON response and get clean TypeScript interfaces or types, with nested objects, merged array items, optional fields and unions inferred for you.

Declaration style

TypeScript

export interface Root {
	id: number
	customer: Customer
	items: Item[]
	tags: (string | number)[]
	coupon: null
	createdAt: string
}

export interface Customer {
	name: string
	email: string
	vip: boolean
}

export interface Item {
	sku: string
	qty: number
	price: number
	note?: string
}

Keys missing from some array items become optional (key?), mixed values become unions, and empty arrays become unknown[]. One sample cannot show every case, so review the result against your API docs.

How to use it.

  1. 01

    Paste a JSON sample, ideally a real API response with arrays that contain several items.

  2. 02

    Set the root name and choose interface or type, with or without export.

  3. 03

    Copy the generated declarations into your codebase, then tighten names, literal types and nullability by hand.

What it does.

Everything this tool handles, all of it inside your browser tab.

  • Nested objects become their own named interfaces
  • Objects in arrays are merged, and keys missing from some items become optional
  • Mixed values become union types, such as string | number
  • Choose interface or type alias, with or without export
  • Custom root name, with singular names for array items
  • Invalid identifiers are quoted and duplicate names get a numeric suffix
  • Invalid JSON is reported with its line and column
  • Runs entirely in your browser: nothing is uploaded

Worked examples.

  • JSON to TypeScript interface with nested objects and arrays

    // JSON
    {
      "id": 4821,
      "customer": { "name": "Asha", "email": "asha@example.com", "vip": true },
      "items": [
        { "sku": "QR-STAND", "qty": 2, "price": 349.5 },
        { "sku": "MENU-PRINT", "qty": 1, "price": 120, "note": "Matte finish" }
      ],
      "tags": ["dine-in", 12],
      "coupon": null
    }
    
    // TypeScript (root name: Order)
    export interface Order {
      id: number
      customer: Customer
      items: Item[]
      tags: (string | number)[]
      coupon: null
    }
    
    export interface Customer {
      name: string
      email: string
      vip: boolean
    }
    
    export interface Item {
      sku: string
      qty: number
      price: number
      note?: string
    }

    customer gets its own interface, items is singularised to Item, note is optional because only one item has it, and the mixed tags array becomes a union array.

  • Top-level array, nulls and awkward keys

    // JSON (root name: Users)
    [
      { "id": 1, "name": "Asha", "first-name": "Asha", "roles": [] },
      { "id": 2, "name": null }
    ]
    
    // TypeScript
    export type Users = UsersItem[]
    
    export interface UsersItem {
      id: number
      name: string | null
      "first-name"?: string
      roles?: unknown[]
    }

    name is a string in one item and null in the other, so it becomes string | null. The hyphenated key is quoted, and the empty roles array becomes unknown[] until you know what it holds.

  • Generate a type alias instead of an interface

    // JSON (root name: Post, style: type)
    { "id": 1, "title": "Hello" }
    
    // TypeScript
    export type Post = {
      id: number
      title: string
    }

    Switch the style to type if your lint rules prefer type aliases. Untick export for declarations used in a single file.

  • Refine the generated types by hand

    // generated
    status: string
    coupon: null
    location: number[]
    
    // refined
    status: "pending" | "paid" | "refunded"
    coupon: Coupon | null
    location: [lat: number, lng: number]

    A sample cannot reveal the full set of status values, the real type behind a null, or that an array has a fixed length. These edits take a minute and catch real bugs.

  • Typing a fetch response safely

    // trusts the server blindly: compile-time only
    const order = (await res.json()) as Order
    
    // validates at runtime, then types follow from the schema
    const order = OrderSchema.parse(await res.json())
    type Order = z.infer<typeof OrderSchema>

    Use generated interfaces for internal code and a runtime schema at the edges where data comes in from outside.

How JSON maps to TypeScript types

JSON has only six kinds of value, and each maps to a TypeScript type. Strings become string, numbers become number, true and false become boolean, null stays null, arrays become T[] and objects become interfaces. TypeScript has no separate integer type, so 42 and 349.5 are both number.

JSON has no date type. Dates arrive as strings such as 2026-10-10T09:30:00Z and are typed as string; convert them to Date objects deliberately when you parse a response. Large integers are another trap: JSON.parse turns every number into a JavaScript double, which is exact only up to 2⁵³ − 1 (9,007,199,254,740,991). 64-bit IDs beyond that are silently rounded, so APIs should send them as strings and your types should say string.

Optional and nullable mean different things. note?: string says the key may be absent; note: string | null says the key is always present but may be null. JSON cannot contain undefined at all, because JSON.stringify drops such keys, so an API that omits empty fields and one that sends null need different types. With the exactOptionalPropertyTypes compiler option, TypeScript also tells a missing key apart from one explicitly set to undefined.

How this converter infers types

Every object becomes its own named declaration, named after its key in PascalCase, so a customer key produces interface Customer. Items in an array named with a plural are singularised: items gives Item and categories gives Category. If two objects would share a name, the second gets a numeric suffix, such as Item2.

Objects inside an array are merged into one shape. A key that is missing from some items becomes optional (note?: string), and a key whose values differ becomes a union such as number | string. Mixed arrays become union arrays with the parentheses TypeScript needs, such as (string | number)[]. A top-level array produces a type alias, so pasting a list of users gives type Users = UsersItem[] plus the item interface.

Empty arrays become unknown[] and empty objects Record<string, unknown>, because a sample cannot show what they would hold. Keys that are not valid identifiers, such as first-name or 2fa, are quoted. If the JSON is invalid, you get the error with its exact line and column instead of a guess; the JSON formatter can help fix it.

The limits of generating types from one JSON sample

Generated types describe the sample you pasted, not every response the API can send. A field that is a string today may be null tomorrow; a field that happens to be present in every item may still be optional. Paste a response with several varied items to improve the merge, and check optional fields and nullability against the API documentation.

Inference cannot see intent. A status of 'paid' becomes string, not the union 'pending' | 'paid' | 'refunded'; narrow it by hand. A fixed-position array like [51.5, -0.12] becomes number[], not the tuple [number, number]. An object keyed by IDs, such as { 'u_1': {...}, 'u_2': {...} }, produces a fixed interface with those exact keys when you really want Record<string, User>. A field that is null in the sample is typed null; replace it with the real type, such as Coupon | null. Responses that differ by a kind or type field are best modelled as a discriminated union, which you write once by hand.

interface vs type: which should you use?

For object shapes, both work and compile to nothing at runtime. Interfaces can be extended with extends and can merge when declared twice, which libraries use to let you augment their types, and they often produce clearer error messages. Type aliases can describe anything, including unions, tuples, mapped and conditional types, and they cannot be reopened after declaration.

A sensible rule is to use interfaces for object shapes and type aliases for unions and computed types, but consistency within a codebase matters more than the choice. Pick the option above that matches your lint rules; the top-level alias for arrays and primitives is always a type, because an interface cannot describe them.

Types are compile-time only: validate at runtime

TypeScript types are erased when your code is compiled. Writing const order = (await res.json()) as Order does not check anything; it tells the compiler to trust you. If the API changes or returns an error body, the mismatch surfaces later as undefined errors far from the cause.

For data that crosses a trust boundary, such as API responses, webhooks, form posts and files, validate at runtime with a schema library such as Zod or Valibot and derive the type from the schema, so the validation and the type cannot drift apart. When the API publishes an OpenAPI document or a GraphQL schema, generate types from that contract instead of from a sample: it covers every field, including ones your sample did not contain.

A practical workflow

Capture a real response from your browser's network tab or a curl call. Remove secrets and personal data, though this page runs entirely in your browser and never uploads what you paste. Paste it here with a meaningful root name such as Order or InvoiceResponse, copy the result into a types file next to the code that calls the API, then refine literal unions, tuples, records and nullability. When the API changes, paste a fresh response and diff the old and new types with the text diff checker to see exactly what moved. If you are deciding whether TypeScript is worth adopting at all, see TypeScript vs JavaScript.

Questions, answered

Something else on your mind? Ask a consultant and get a reply within one business day.

How do I convert JSON to a TypeScript interface?

Paste the JSON above, set a root name such as Order, and copy the generated interfaces. Each nested object becomes its own interface, and array items are merged into one shape.

How are optional fields detected?

When an array contains several objects, their shapes are merged. Any key that is not present in every item is marked optional with a question mark, so paste a sample with varied items for the best result.

Why is a field typed as null or unknown[]?

The sample only contained null or an empty array, so there was nothing to infer from. Replace it with the real type, such as string | null or Tag[], or paste a sample where the field has a value.

Should I use interface or type?

Both work for object shapes. Interfaces can be extended and merged and give clearer error messages; type aliases are needed for unions, tuples and mapped types. Pick whichever your codebase already uses.

Does it handle deeply nested JSON and top-level arrays?

Yes. Each nested object gets its own interface, names are de-duplicated with a numeric suffix, and a top-level array becomes a type alias such as type Users = UsersItem[].

Can it generate enums or literal types?

No. Strings are typed as string because one sample cannot show every allowed value. Narrow fields such as status to a union of literals, like 'pending' | 'paid', by hand.

Do generated types validate data at runtime?

No. TypeScript types are removed at compile time. For API responses and other untrusted input, validate with a runtime schema library and derive the type from it.

How are dates and large numbers handled?

Dates in JSON are strings, so they are typed string. Integers above 2⁵³ − 1 lose precision when parsed in JavaScript, so APIs should send large IDs as strings.

How is this different from quicktype?

quicktype is a broader code generator that targets many languages and can produce runtime converters. This tool focuses on fast, readable TypeScript declarations from a pasted sample, with nothing to install.

Is my JSON uploaded?

No. Parsing and type generation run in your browser, so it is safe for internal API payloads. Still remove secrets before sharing the generated types.

More free tools.

All tools

Need tooling like this inside your product?

We build internal tools, developer platforms and APIs. Tell us what your team keeps doing by hand.