Code

JSON → TypeScript Generator

Paste a raw API response and instantly generate clean TypeScript definitions and production-ready Zod validation schemas.

Source JSON

10 line(s)

What you get

  • Interface or type alias generation from nested JSON.
  • Matching z.object schema for runtime safety.
  • Support for mixed arrays and optional fields.

Recommended flow

  • Paste API response sample JSON.
  • Tune root name, export style, and null behavior.
  • Copy TypeScript and Zod into your app modules.

Privacy-first

Everything runs locally in your browser. No JSON payloads are uploaded or stored.

One JSON sample, not a schema: how the inference actually works

The generator reads exactly the JSON pasted in — one concrete value, parsed once — and walks it recursively to decide a TypeScript type for every string, number, boolean, null, array, and nested object it finds. There is no merging of multiple examples and no separate schema definition anywhere: whatever shape happens to be in that one sample is the only evidence the generator ever has, which is both the whole appeal (paste and go) and the source of every limitation below.

Formatted JSON from the JSON Validator & Formatter tool can be handed straight into this generator without retyping it, using the same browser-storage handoff mechanism the site's other JSON tools share.

Nested objects, arrays, and mixed types: what gets inlined and what gets a union

A nested object is inlined anonymously at the point it is found and never lifted into its own named interface, no matter how deep the nesting goes — a three-level-deep object produces one large interface literal with three levels of indentation, not separate named types. An array with one consistent element type becomes T[]; an empty array becomes unknown[] because an empty sample carries no type evidence at all; and a mixed-type array — say, a list containing a number, a string, and a boolean — becomes a deduplicated union in the order those types first appear, wrapped in parentheses: (number | string | boolean)[].

The one thing to know before trusting a null field's generated type

A field whose sample value is null always generates as type null, in both the Nullable and Optional null-handling modes — the two modes do not actually differ in what type gets written. What Optional changes is only whether a question mark is added to the property name (avatar_url?: null instead of avatar_url: null); it never widens the type to include whatever the real, non-null value would have been, because a single null sample gives the generator no way to know what that real type is. Picking Optional for a field expected to sometimes hold a string does not produce avatar_url?: string — it produces avatar_url?: null, which TypeScript will only ever allow null or undefined to be assigned to.

TypeScript output options: interface vs. type, and what the root name controls

Export Style only changes the two keywords wrapping an otherwise identical body: export interface Name against export type Name = {...}. Root Type Name renames the generated interface or type directly, and separately drives the Zod schema's constant name — a root of User produces export const userSchema = ... and, if the include-infer option stays on, an appended export type User = z.infer<typeof userSchema> that mirrors the interface's own name.

Object keys that would not be valid as bare JavaScript identifiers — starting with a digit, or containing a hyphen or space — are automatically wrapped in quotes in both the TypeScript and Zod output, so a key like user-id becomes 'user-id': string rather than producing invalid syntax.

The matching Zod schema, and where it makes a different assumption than the TypeScript output

Every primitive maps to its obvious Zod call — z.string(), z.boolean() — except numbers, which the Zod side actually distinguishes and the TypeScript side does not: a whole number like 1 generates z.number().int(), while a decimal like 9.99 generates plain z.number(), a distinction TypeScript's own number type has no way to express either way.

For a null-valued field specifically, the Zod output makes an assumption the TypeScript output does not: rather than emitting a null schema, it hardcodes z.string().nullable() (or z.string().optional() in Optional mode) — assuming the real underlying type would have been a string, which is frequently the case for an unset text field but is never actually verified from a single null sample. The same input field can therefore generate as null on the TypeScript side and as a nullable string on the Zod side, from the exact same JSON.

The exact interface and schema this page generates by default

The page loads with a sample object already in the editor — an id, an email, an is_active flag, a nested profile object with a name and a null avatar_url, and a roles array of two strings. With the default settings (Interface export, Nullable), that sample generates the fields listed below, with the nested profile object inlined right there rather than extracted.

The matching default Zod schema differs on exactly the field the previous section flags: it types avatar_url as z.string().nullable() while the interface types the same field as plain null, and because Include z.infer<> stays checked by default, the schema is followed by export type ApiResponse = z.infer<typeof apiResponseSchema> — a type derived from the schema rather than hand-written to match the interface above it.

  • id: number — a whole number reads the same as a decimal would on the TypeScript side
  • email: string
  • is_active: boolean
  • profile: { name: string; avatar_url: null } — inlined anonymously, never lifted into its own interface
  • roles: string[] — one consistent element type

Common use cases

Turning a real API response into types in one paste

Copy a response body straight from a network tab, paste it here, and get an interface plus a runtime-checked Zod schema together.

Prototyping a data shape before wiring up validation

Sketch out an approximate JSON shape by hand and generate a starting interface to build against immediately.

Auditing which fields would need a real optional type

Spot every null-valued field this page flags as null-typed, then decide by hand which ones actually need a proper nullable type rather than accepting the generated placeholder.

Generating a quick, throwaway type for a one-off script

Skip hand-writing an interface for a script that runs once by pasting its expected input shape here instead.

Frequently asked questions

Does the generator merge multiple JSON samples to build a more complete type?

No — it reads exactly one pasted value and infers types from that single sample alone. If a field is sometimes a string and sometimes null across different real responses, only whichever one is present in the sample pasted gets reflected.

What type does a field get if its value is null in my sample?

It generates as the literal type null in the TypeScript output, in both Nullable and Optional modes — Optional only adds a question mark to the property, it does not widen the type.

Why does choosing Optional for null handling still type the field as null instead of removing the restriction?

Because the generator has no evidence of what the real type would be from a null sample alone, so it can only mark the field as not required — it cannot recover a type it was never shown. The field ends up optional but still restricted to null or undefined.

How does a mixed-type array get typed?

As a deduplicated union of every distinct element type it finds, in first-seen order, wrapped in parentheses and followed by [] — for example (number | string | boolean)[] for an array holding one of each.

What happens with an empty array in my sample?

It generates as unknown[], since an empty array provides no element to infer a type from at all.

Are nested objects extracted into their own named interfaces?

No — every nested object is inlined exactly where it appears, indented one level deeper, no matter how many levels deep the nesting goes. Nothing is ever hoisted out into a separate, named type.

Does the Zod schema for a null field assume it is a string, even if it is not?

Yes, specifically — a null-valued field always generates z.string().nullable() (or .optional()) on the Zod side, regardless of what the field's real type would be, because that assumption is hardcoded for exactly this case rather than inferred.

Is my pasted JSON ever sent anywhere?

No — parsing and generation both run as pure JavaScript in the browser with no network request involved, so nothing pasted into the editor leaves the device.

You might also like