Skip to content

Tools

JSON to TypeScript

Paste JSON, get TypeScript interfaces that tell the truth —? and | null kept distinct, your real keys preserved, optional runtime validators. Runs entirely in your browser.

Output:

Generated code

Beta — the generated code is unit- and compile-tested, but inference from one sample can't be perfect; on rare occasions the output may need a tweak. Spot something wrong?Tell me.

What is this tool?

A free JSON-to-TypeScript converter. Paste any JSON document and it generates interfaces (or type aliases) for the whole tree — nested objects become their own named types, arrays become typed lists, and every place your sample was too thin to type is flagged with a warning instead of silently guessed. Optionally it also emits runtime validators: plain functions that check the shape of unknown data and throw with the exact JSON path that failed.

Your JSON never leaves the browser. Inference and generation are pure client-side string work; the page functions offline once loaded.

How to use it

  • Paste JSON on the left — the TypeScript regenerates as you type.
  • Name the root type; nested types are named from their keys (users: […]User).
  • Choose interfaces or type aliases — same shapes either way.
  • Flip on runtime validators if the data crosses a trust boundary (an API you don't own, a file a user uploads).
  • Copy or Download the .ts file.

Why the field names mirror your JSON exactly

Most converters "helpfully" rename created-at tocreatedAt — and quietly break the contract, becauseJSON.parse returns the original keys. Casting that object to a renamed interface produces types that lie:response.createdAt compiles and isundefined at runtime. This tool keeps your keys verbatim, quoting the ones that need it:

export interface Response {
  id: number;
  /** ISO 8601 date-time string */
  "created-at": string;
}

const r = JSON.parse(raw) as Response; // actually true
r["created-at"]; // string

That's what makes as Response honest. If you want camelCase domain objects, that's a mapping step — one this page won't pretend doesn't exist.

? and | null are not the same thing

TypeScript can express the difference between a key that may beabsent and a key that is present but null — and JSON APIs routinely use both. This converter keeps them apart, because they behave differently in code:

Sample evidenceGeneratedWhat it means
key missing from one object in a listlast_login?: string;"last_login" in obj may be false; obj.last_login may be undefined
value explicitly nullnote: string | null;the key is always there; its value may be null
both observedx?: string | null;brace for either

Flattening these into one (as most converters do) costs you real checks: obj.x === null and obj.x === undefined are different bugs.

Why dates stay string

An ISO timestamp in JSON is still a string afterJSON.parse — nothing revives it into aDate. Typing it Date would be another lie. Detected date fields are typed string and marked with a /** ISO 8601 date-time string */ doc comment, so you know to new Date(x) at the use site (or in your mapping layer).

The runtime validators — a poor man's zod

Types vanish at runtime; data from outside doesn't care about your interfaces. With Runtime validators on, each type gets a checker:

const user = toUser(await res.json());
// TypeError: expected an integer at $.users[3].id, got a string

The checks match the inference exactly — integers are verified with Number.isInteger, optional keys may be absent, nullable ones may be null — and the thrown message carries the JSON path, which is most of what you reach for zod for. When you outgrow them (transformations, unions, custom refinements), graduate to a real schema library; until then, zero dependencies.

Interface or type alias?

For object shapes they're interchangeable; pick your codebase's house style. Interfaces give marginally better error messages and can be augmented; type aliases compose with unions and mapped types. The tool emits either — the shapes are identical.

FAQ

Is it free? Yes — free, no sign-up, no limits.

Is my JSON uploaded anywhere? No — fully client-side, works offline after load, and only your last input is kept (in your own browser's local storage).

Why did a field come out unknown?The sample never showed its type: always null, an empty array, or two samples with irreconcilable types. The warning names the path.unknown (not any) is deliberate — it forces a check before use instead of disabling the type system.

Why number for both 1 and1.5? TypeScript has no integer type. The distinction isn't wasted though — the runtime validators check integer fields with Number.isInteger, and the siblingDart,Python,Go,Kotlin andSwift outputs type them properly.

Can it read a JSON Schema? Not yet — v1 infers from samples; schema input and zod output are planned.

Built from scratch for this site in vanilla TypeScript — a pure, unit-tested inference engine with golden-tested emitters and zero runtime dependencies.