JSON to TypeScript Converter

Convert JSON to TypeScript instantly: paste JSON and this free JSON to TypeScript converter generates interfaces (or type aliases) with optional properties, unions and null handling, plus a bonus Zod schema — all in your browser.

Paste JSON, upload a file or press Sample. It converts as you type.

Private by design: conversion runs in your browser and nothing is uploaded or saved. Only your option choices (root name, export, readonly, output mode) are remembered on this device.

How to convert JSON to TypeScript

  1. Paste JSON into the left box, drop a .json file on it, or press Upload. If the JSON has a syntax error, the status line shows the line and column — format and validate it first if it needs more cleaning up.
  2. The TypeScript appears on the right as you type. Set a root name for the top-level type, choose interface or type alias, and toggle export, readonly and unknown[] for empty arrays as needed.
  3. Switch Output to Zod schema for a matching runtime validator instead of (or to copy alongside) the TypeScript.
  4. Press Copy, or Download to save it as a .ts file.

JSON to TypeScript example

This JSON has a nested object, an array of objects with an inconsistent key, and a null value:

{
  "id": 101,
  "name": "Ada Lovelace",
  "active": true,
  "roles": ["admin", "editor"],
  "address": { "city": "London", "zip": "W1A 1AA", "country": null },
  "orders": [
    { "id": 1, "total": 42.5, "note": "Gift wrap" },
    { "id": 2, "total": 17 }
  ]
}

converts to this TypeScript. Notice note becomes optional (it's missing from the second order), country is null, and Address/Order (singularized from the orders array) become their own named interfaces:

export interface Address {
  city: string;
  zip: string;
  country: null;
}

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

export interface Root {
  id: number;
  name: string;
  active: boolean;
  roles: string[];
  address: Address;
  orders: Order[];
}

Arrays, unions and optional properties, explained

When an array's items are objects, the converter doesn't create one interface per item — it merges every item into a single shape:

  • A key present in every item is required. If its value's type differs between items, the property becomes a union: id: number | string.
  • A key missing from some items becomes optional: note?: string — exactly what happened to note above.
  • A mixed-type array of primitives (not objects) becomes a union array. [1, "two", true] converts to:
export type Root = (number | string | boolean)[];

How null is handled

A property that is only ever null becomes field: null. A property that is null in some places and something else in others becomes a union, like country: string | null in the example above — the convention TypeScript's strictNullChecks expects, and the same pattern most hand-written API types use.

Interface vs type in TypeScript

For a plain object shape the two are nearly interchangeable. With the type alias option, the same small example converts to:

export type Root = {
  active: boolean;
};

Interfaces can be extended later by re-declaring them (declaration merging) and read slightly more clearly in editor tooltips; type aliases can additionally name unions and tuples directly. Pick whichever matches your codebase's convention — most teams standardise on one for generated data shapes.

Naming, quoting and de-duplication rules

Interface names come from the property key, PascalCased. Keys that are not valid TypeScript identifiers are quoted automatically:

export interface Root {
  "user-id": number;
  "2fa": boolean;
  display_name: string;
}

Structurally identical shapes are de-duplicated to one interface, wherever they occur. Here a and b hold the exact same shape ({ x: number }), so both reference the same interface instead of two nearly-identical ones:

export interface A {
  x: number;
}

export interface Root {
  a: A;
  b: A;
}

JSON to Zod schema (bonus)

Switching Output to Zod schema generates a matching Zod validator instead of (or alongside) the TypeScript, so you can validate data you don't control — an API response, webhook payload or form submission — at runtime, not just check it at compile time:

import { z } from 'zod';

export const AddressSchema = z.object({
  city: z.string(),
  zip: z.string(),
  country: z.null(),
});

export const OrderSchema = z.object({
  id: z.number(),
  total: z.number(),
  note: z.string().optional(),
});

export const RootSchema = z.object({
  id: z.number(),
  name: z.string(),
  active: z.boolean(),
  roles: z.array(z.string()),
  address: AddressSchema,
  orders: z.array(OrderSchema),
});

Pair it with z.infer<typeof RootSchema> to get the TypeScript type back out of the schema itself, so the two never drift apart.

JSON to TypeScript in your editor or from the command line

  • Quicktype (multi-language, more setup): npm install -g quicktype, then quicktype input.json -o output.ts.
  • json-schema-to-typescript (starts from a JSON Schema, not raw JSON): npx json-schema-to-typescript schema.json > output.ts.
  • VS Code: the "JSON to TS" extension pastes interfaces from your clipboard directly into a file.
  • This page: no install, works from raw JSON directly, and adds a Zod schema in the same click.

Related developer tools

Frequently asked questions

How do I convert JSON to TypeScript?

Paste your JSON into the left box, drop a .json file on it, or press Upload — the TypeScript appears on the right as you type. Nested objects become their own named interfaces automatically; there is nothing to configure to get a working result, though the options let you switch to type aliases, add export/readonly, and change the root name.

Interface vs type in TypeScript: which should I use?

For object shapes like these, they're nearly interchangeable: interface Foo { a: string } and type Foo = { a: string } behave the same for objects. Interfaces can be re-opened and extended later (declaration merging) and give slightly clearer error messages; type aliases can also name unions, tuples and primitives, which interfaces cannot. This tool defaults to interface (the more common convention for API/data shapes) with a "type alias" option if you prefer types everywhere.

Why does one of my properties have a "?" (become optional)?

That happens when you convert an array of objects and a key is present in some items but missing from others — the generated interface describes every item in the array, so a key that is not always there becomes optional (key?: T) rather than required. A key present in every item, even with different value types, stays required and becomes a union instead (e.g. id: number | string).

How does the converter handle null?

A value that is only ever null becomes that property's whole type: field: null. A value that is sometimes null and sometimes something else (in one object, or across an array's items) becomes a union with null, e.g. country: string | null — the same convention TypeScript's strictNullChecks expects.

What happens with an empty array or object?

An empty array ([]) has no elements to infer a type from, so it becomes any[] by default — tick "unknown[] for empty arrays" to get the stricter unknown[] instead, which forces you to narrow the type before using it. An empty object ({}) becomes its own interface with no properties: interface Foo {}.

Why are some of my property names in quotes?

JSON allows any string as a key, but a TypeScript interface property name must be a valid identifier to be written bare. Keys with hyphens, spaces, or a leading digit — like "user-id" or "2fa" — are automatically quoted ("user-id": number;) so the generated code is always valid; ordinary keys like display_name are left unquoted.

Can I generate a Zod schema instead of (or alongside) the TypeScript?

Yes — switch "Output" to "Zod schema". It mirrors the same interfaces as z.object({...}) schemas (with .optional() where a property is optional), so you get runtime validation that matches the generated types, not just compile-time types. This is a bonus, best-effort feature: for very unusual shapes, review the output before relying on it in production.

Does this handle deeply nested JSON, or arrays of arrays?

Yes. Every level of nesting gets its own named interface, referenced from its parent, and arrays of arrays become T[][] (and deeper, if needed). There is no depth limit beyond what your browser can parse and render.

Is my JSON uploaded anywhere?

No. Parsing and type generation run entirely in this browser tab's JavaScript; nothing you paste, upload or drop is sent to a server, logged, or saved. Only your option choices (root name, export keyword, readonly, output mode) are remembered in this browser's local storage.

How is this different from Quicktype or json-schema-to-typescript?

Quicktype and json-schema-to-typescript are excellent, more heavyweight tools (Quicktype also targets many other languages; json-schema-to-typescript starts from a JSON Schema, not raw JSON). This tool is a lighter, single-purpose page: no install, no account, generates from raw JSON directly, and adds a Zod schema alongside the TypeScript in one click. For very large schemas or multi-language output, Quicktype's CLI (npm install -g quicktype) is worth reaching for.

This converter is provided as is. Review generated types (and Zod schemas) before relying on them in production, especially for JSON whose shape varies more than the sample you convert. Spotted a wrong result? Tell us. Last reviewed .