Skip to content
Back to Blog
Tutorials

JSON to TypeScript: Generate Interfaces & Types (2026 Guide)

Convert JSON to TypeScript interfaces the right way: type inference rules, interface vs type, optional and union fields, plus pitfalls to avoid. Try it free.

11 min read

JSON to TypeScript: Generate Interfaces & Types (2026 Guide)

The fastest way to go from JSON to TypeScript is to paste your payload into a JSON to TypeScript converter and copy the interface it generates. Nothing to install, nothing uploaded, all done in your browser. That handles the “I need types right now” case.

But generating types and generating good types are two different things. A converter has to guess. Is this field optional or just missing from one sample? Is that array always strings, or sometimes numbers too? Should the date string become a Date? Below is how the inference actually works, when to reach for interface versus type, and the pitfalls of turning live API responses into types you can trust.

How to convert JSON to TypeScript

Converting JSON to TypeScript takes three steps:

  1. Paste your JSON. Drop an object, array, or raw API response into the input box. Conversion runs instantly, entirely client-side.
  2. Tune the output. Pick interface or type, set a root name like User or ApiResponse, toggle the export keyword, and choose ?: or | null for optional fields.
  3. Copy or download. Grab the generated TypeScript with one click and paste it straight into your codebase.

That’s it for the transactional path. The JSON to TypeScript converter reads the input, infers a type tree, and prints definitions on the right, and nothing leaves the page. What follows is how to read that output so you can fix the cases it can’t guess.

How JSON types map to TypeScript

Every JSON value has a TypeScript counterpart. The mapping is mostly one-to-one:

JSON valueTypeScript type
"text"string
42, 3.14number
true / falseboolean
nullnull
[1, 2, 3]number[]
{ ... }interface or type

Objects are where the work happens. Take a typical REST user payload:

{
  "id": 101,
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "active": true,
  "roles": ["admin", "user"]
}

A converter produces this TypeScript interface from the JSON:

export interface User {
  id: number;
  name: string;
  email: string;
  active: boolean;
  roles: string[];
}

Each scalar maps to its primitive type, and the roles array becomes string[]. Drop that into your API client and you get autocomplete and compile-time checks for free.

How the converter infers types

The inference algorithm is where most of the guessing happens. Four rules cover almost everything you’ll throw at it.

Structural inference: one named interface per object

Each distinct object shape becomes its own named interface. Nested objects don’t collapse into inline types. They get hoisted into separate, referenced definitions:

{
  "order": {
    "id": "A-1",
    "total": 42.5,
    "customer": { "name": "Sam", "vip": false }
  }
}
export interface Root {
  order: Order;
}

export interface Order {
  id: string;
  total: number;
  customer: Customer;
}

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

Identical shapes are deduplicated, so two fields with the same structure share a single interface instead of producing copies.

Array merging: inferring optional fields

When you pass an array of objects, the converter merges them key by key. If a key appears in some elements but not others, it’s marked optional:

{
  "users": [
    { "id": 1, "nick": "x" },
    { "id": 2 }
  ]
}
export interface Root {
  users: User[];
}

export interface User {
  id: number;
  nick?: string;
}

id is in every element, so it stays required. nick is missing from the second user, so it becomes nick?: string. This is why a single sample is rarely enough, as the pitfalls section below explains.

Union types for mixed arrays

Arrays don’t have to be homogeneous. When elements have different types, the converter collects them into a union:

{
  "tags": ["a", "b"],
  "meta": [1, "two"]
}
export interface Root {
  tags: string[];
  meta: (string | number)[];
}

tags is uniformly strings, so string[]. meta mixes a number and a string, producing (string | number)[]. An empty array can’t be inferred at all and falls back to unknown[], which is the honest answer when there’s nothing to learn from zero elements.

Optional vs null: ?: versus | null

These look similar but mean different things. A field marked ?: may be absent from the object entirely. A field typed | null is always present but may hold the value null:

{ "score": null }
export interface Root {
  score: number | null;
}

Here score is present with an explicit null, so it’s typed number | null. The value exists, it’s just null. Contrast that with nick?: string from the array example, where the key can be missing outright. Reach for ?: when the API may omit the key, and | null when it sends the key with a null value. Most converters let you control how this renders with a toggle.

interface vs type: which should you use?

Both can describe an object shape. Here’s how to decide:

NeedUse
Plain object shape from JSONinterface
Union (A | B) or intersection (A & C)type
Declaration merging / extending across filesinterface
Mapped or conditional typestype
Slightly cleaner editor error messagesinterface

For data that came out of a JSON object, interface is the conventional choice and produces marginally nicer compiler errors. The moment you need a union, say a Result that’s either a success or an error, you have to switch to a type, because interfaces can’t express unions:

type ApiResult =
  | { status: "ok"; data: User }
  | { status: "error"; message: string };

A good default: generate an interface for plain JSON objects, and convert to a type when the shape needs a union or intersection. The TypeScript Handbook covers the edge cases if you want the full comparison. When you need a json to typescript type alias instead of an interface, most converters expose a single toggle to flip the entire output.

quicktype vs json-to-ts vs online tools vs manual

There’s no single best way to generate TypeScript types. It depends on where the JSON lives and how often it changes.

ApproachBest forTrade-off
quicktypeMulti-language output, generating runtime validation codeHeavier; more output than you often need
json-to-ts (library)Build-time codegen wired into a script or pipelineRequires setup and a Node toolchain
Browser-based online toolOne-off conversions, sensitive payloads, zero installManual paste each time
Writing types by handTiny objects, learning the type systemTedious and error-prone at scale

Pick quicktype when you need types in several languages or want it to emit validators alongside the types. Pick the json-to-ts library when the conversion belongs in a build step. For a quick conversion, especially of a payload containing tokens, customer IDs, or anything you’d rather not paste into a remote service, a browser tool wins on privacy and speed. Our JSON to TypeScript converter runs fully client-side, so the JSON never leaves the page and there’s nothing to install or update.

Types aren’t validation: bridging to runtime checks

This trips up a lot of teams. TypeScript types are erased at compile time and do nothing at runtime. A generated interface User will not check that an actual API response matches it. If the server sends id as a string instead of a number, TypeScript happily believes your type annotation while the real data lies.

To generate typescript types and enforce them on live data, pair the types with a runtime validator:

  • zod lets you define a schema once, infer the static type from it, and parse responses at runtime.
  • io-ts does codec-based validation and is popular in functional codebases.
  • JSON Schema with Ajv gives you language-agnostic schemas validated at runtime, useful when the contract is shared across services.

A common pattern is to generate the interface for editor support, then write a matching zod schema as the actual boundary check:

import { z } from "zod";

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  active: z.boolean(),
  roles: z.array(z.string()),
});

type User = z.infer<typeof UserSchema>;

// Throws if the real response doesn't match the schema.
const user = UserSchema.parse(await res.json());

If you’re going the schema route, our JSON Schema validation guide walks through writing and enforcing schemas end to end.

Common pitfalls when typing JSON

Generated types are a starting point, not a finished product. Watch for these.

snake_case vs camelCase. Converters keep keys verbatim. A GitHub-style payload with public_repos produces public_repos: number, not publicRepos. If your codebase prefers camelCase, you’ll either live with the snake_case types or add a mapping layer that renames fields on the way in.

Date strings stay string. A field like "2026-06-01T00:00:00Z" is typed as string, not Date. That’s deliberate, since JSON has no date type and guessing wrong is worse than being honest. The string-to-Date conversion belongs in your parsing layer, where you control it.

any and unknown leakage. Empty arrays become unknown[] and deeply mixed structures can degrade into loose types. That’s not a bug. It’s the converter admitting it can’t infer from insufficient data. Tighten them by hand once you know the real shape.

Deeply nested explosion. A heavily nested payload generates a long chain of small interfaces. That’s usually fine and more readable than one giant inline type, but it’s worth flattening shapes that don’t earn their own name.

The single-sample trap. This one catches almost everyone. Optional fields can only be inferred from multiple array elements, and a single object can’t tell the converter which keys are sometimes absent. Collect a representative array of responses first. A JSON formatter is handy for pasting and inspecting several samples side by side before you convert, and if you’re feeding non-standard input like JSONC, read our JSON5 / JSONC guide, because the converter needs strictly valid JSON.

Frequently asked questions

How do I convert JSON to a TypeScript interface?

Paste your JSON into a JSON to TypeScript converter. It reads the input in your browser and generates a TypeScript interface instantly. Click Copy to grab the result. There’s no upload, no account, and no plugin to install.

Should I use interface or type for JSON data?

Use interface for plain object shapes from JSON, since it’s conventional and gives slightly clearer editor errors. Switch to type when you need a union or intersection, which interfaces can’t express.

How are nested objects and arrays handled?

Nested objects become separate, named interfaces (an address field yields an Address interface). Arrays of objects are merged key by key into one element interface; primitive arrays become typed arrays like string[].

How are optional and null fields typed?

A key missing from some array elements is marked optional (nick?: string). A key that’s always present but holds null is typed with | null (score: number | null). The two describe different situations: absent versus present-but-null.

Do TypeScript types validate JSON at runtime?

No. TypeScript types are erased at compile time and do not check data at runtime. To validate an actual API response, pair the generated types with a runtime validator like zod, io-ts, or JSON Schema with Ajv.

quicktype vs json-to-ts vs an online converter: which is best?

quicktype suits multi-language output and runtime validators. The json-to-ts library suits build-time codegen. An online converter suits quick, private, one-off conversions with zero install. For sensitive payloads, a client-side browser tool keeps data off any server.

Why are date strings typed as string instead of Date?

JSON has no date type, so a date is just a string on the wire. Typing it as string is honest and stable; converting to Date is a decision for your parsing layer, where you control the format and error handling.

Is my JSON data private when using an online converter?

With a client-side tool, yes. Conversion runs entirely in your browser using JavaScript, so the JSON (including tokens, IDs, and customer data) never leaves the page and is never sent to a server.

Related Articles

View all articles