JSON to TypeScript — Generate TypeScript Interface from JSON

Automatically generate TypeScript interfaces from any JSON structure. Handles nested objects, arrays, and multiple types. Copy the output directly into your TypeScript project. 100% client-side — your data stays private.

JSON to TypeScript Generator

JSON Input
TypeScript Output Ready

How do you convert JSON to a TypeScript interface? Parse the JSON, then map each value to the TypeScript type that describes it: strings become string, numbers number, booleans boolean, nested objects their own named interface, and arrays the element type followed by []. Because JSON carries values and not a schema, the result describes the one sample you supplied — it is a starting point you refine, not a guaranteed contract.

How to Use the JSON to TypeScript Converter

  1. Paste a JSON object or array — The input must be a single valid object or array. A bare string or number is rejected — there is no interface to describe. Load Sample fills in a nested example if you want to see the output shape first.
  2. Pick a representative sample — Only the first element of any array is inspected, so choose a record where every optional field is populated. A sample whose first item omits a property produces an interface missing that property entirely.
  3. Name the root interface — The Root name box seeds every generated name. RootObject becomes IRootObject; typing User gives you IUser. Nested interfaces take their names from their own keys regardless of what you put here.
  4. Click Generate — The status tag next to the output reports how many interfaces were produced. Nested interfaces are printed before the root, which is only a readability detail — TypeScript resolves interfaces regardless of the order they appear in.
  5. Fix the types the sample could not show — Widen null fields to something like string | null, mark genuinely optional keys with ?, and replace fixed strings with a union or an enum where the API only allows certain values.
  6. Copy the output into your project — Paste it into a .ts file and add export to each interface you need elsewhere — the generated code has no export keywords.

How the Type Inference Works

The converter runs JSON.parse on your input and then walks the resulting value, deciding a TypeScript type for every property it meets. JSON has six value kinds and TypeScript has a direct equivalent for most of them, so the mapping is largely mechanical.

JSON valueGenerated typeNote
"Alice"stringDates, UUIDs and enum-like values all arrive as strings and are typed as such.
30 or 1.5numberTypeScript has one numeric type, so integers and floats are indistinguishable here.
trueboolean
nullnullLiterally the null type, which accepts nothing else. Almost always needs widening by hand.
{ "city": "NYC" }IAddressA new interface named from the property key, emitted separately.
["a", "b"]string[]Element type taken from the first item only.
[{ "id": 1 }]IPosts[]An interface is generated from the first object and the property typed as an array of it.
[]any[]An empty array carries no type information at all.
[[1, 2]]any[][]Nested arrays are not inspected past the outer level.

How Names Are Chosen

Every interface name is the property key with its first letter capitalised, stripped of anything that is not a letter, digit or underscore, and prefixed with I. So address gives IAddress and user-profile gives IUserprofile. Keys are left alone unless they need quoting: first-name and 2fa are emitted as "first-name" and "2fa", which is valid TypeScript and keeps the property name matching the JSON exactly.

One detail surprises people: names are taken from the key verbatim, with no attempt at singularising. A posts array of objects produces interface IPosts and the type IPosts[], so a single post has the plural type name. Rename it to IPost after pasting if that bothers you — it is a two-second edit and the generated code compiles either way.

Interfaces are deduplicated by name, not by shape. If two different objects in your JSON both sit under a key called meta, only the first one becomes IMeta and the second silently reuses that name despite having different fields. Check the output when a key name repeats at different levels of a document.

A Worked Example

The built-in sample has a nested object, an array of strings and an array of objects. Running it with the default root name produces three interfaces, printed innermost first:

interface IAddress {
  street: string;
  city: string;
  state: string;
  zip: string;
}

interface IPosts {
  title: string;
  body: string;
  likes: number;
}

interface IRootObject {
  id: number;
  name: string;
  email: string;
  isActive: boolean;
  age: number;
  address: IAddress;
  hobbies: string[];
  posts: IPosts[];
}

When the top level is an array rather than an object, you also get a type alias. An array of user objects with the root name User yields interface IUser describing one element plus type User = IUser[]; for the collection.

What a Single Sample Cannot Tell You

Generating types from data rather than from a schema has a hard ceiling: the output describes the example you pasted, and nothing more. Knowing exactly where that ceiling sits is what separates a useful starting point from a type that quietly lies to you.

  • Optional properties look required. Every key in your sample is emitted as required. If the API omits middleName for most users, the generated interface still demands it. Add ? to any field the API may leave out.
  • Nulls collapse to the null type. A field that happened to be null in your sample is typed null, which will reject the string it normally holds. Widen it to string | null — this is the single most common edit needed.
  • Only the first array element is read. An array of mixed shapes is described by its first entry alone. If a list contains both free and paid accounts and the first is free, the paid fields never appear.
  • Unions are never inferred. A status field of "active" becomes string, not "active" | "archived" | "banned". Narrowing those by hand is usually the highest-value change you can make.
  • Dates stay strings. JSON has no date type, so an ISO timestamp is typed string. That is technically correct for the wire format; convert to Date at the boundary rather than pretending the field is already one.

A type is also only a compile-time claim. TypeScript erases interfaces during compilation, so nothing checks at runtime that the response actually matches. Asserting as IUser on a fetch result tells the compiler to stop asking questions; it does not make the data conform. For anything crossing a network boundary, validate the payload with a runtime schema library and derive the type from that schema, so one definition governs both checks.

Where the API publishes a machine-readable description — OpenAPI, JSON Schema, GraphQL — generate from that instead. A schema states which fields are optional, which values are permitted and what each field means, all of which sample data can only hint at. This tool is for the common case where no such description exists and all you have is a response you captured from an endpoint.

Frequently Asked Questions

Each nested object becomes its own interface, named from the property key with the first letter capitalised and an I prefix — an address property produces IAddress, and the parent references it as address: IAddress;. Nesting is followed to any depth, and the inner interfaces are printed before the ones that use them. Order does not matter to the compiler; interfaces are resolved regardless of where they appear in the file.

The element type comes from the first item only. An array of primitives gives string[], number[] or boolean[]. An array of objects generates an interface named after the property — a posts array produces IPosts and the type IPosts[], with no singularisation. An empty array becomes any[] because it carries no type information, and an array of arrays becomes any[][].

Because it was null in the sample you pasted. The converter reports what it sees, and null in TypeScript is a type that accepts only null — so the field will reject the string it normally holds. Widen it by hand to string | null, or paste a sample where that field is populated. This is the most common edit the output needs.

No. Every key present in your sample is emitted as required, and any key absent from it does not appear at all. JSON carries no notion of optionality, so a single example cannot distinguish "always present" from "present this time". Add ? to the fields your API may omit, and prefer a sample record where every optional field is filled in.

That is a house-style question. The I prefix comes from C# and older TypeScript codebases; the TypeScript documentation itself does not use it, and many modern projects name the interface User rather than IUser. The generator applies the prefix consistently, so a find-and-replace strips it in one pass if your project prefers plain names.

For describing the shape of an object, they are close to interchangeable. Interfaces support declaration merging and produce slightly friendlier error messages for object types; type aliases can express unions, intersections and mapped types, which interfaces cannot. This tool emits interfaces for objects and falls back to a type alias when the root is an array, since an array type cannot be written as an interface.

No, and this matters. TypeScript types are erased at compile time, so nothing checks that an actual API response matches the interface. Casting a fetch result with as IUser silences the compiler without verifying anything. For data crossing a network boundary, validate with a runtime schema library and derive the TypeScript type from that schema so a single definition covers both.

No. Parsing and generation run in JavaScript in your tab, so the payload never leaves your machine. Be aware of one thing: pressing Generate writes your input into this page's URL so a result can be bookmarked or shared. If the sample came from a real API response containing tokens or customer data, clear the panel before sharing that link.

Not directly — the input has to be exactly one valid JSON document, because JSON.parse follows the strict grammar. Remove trailing commas, comments and unquoted keys first. For newline-delimited records, wrap them in square brackets separated by commas so the whole thing parses as one array. The JSON Formatter will point at the offending character if parsing fails.

Use Cases

Typing an Undocumented API Response

A third-party endpoint returns forty fields and ships no schema. Capture one response, paste it here, and get the interface in seconds instead of transcribing every key by hand into a .ts file.

Making a Config File Autocomplete

Generate an interface from your existing config.json so the editor offers key names and flags a typo at build time, rather than failing on startup because a setting was misspelled.

Writing Test Fixtures That Compile

Turn a captured payload into an interface, then type your mock objects against it. The compiler now catches a fixture that has drifted from the real response shape instead of letting a stale test pass.

Migrating a JavaScript File to TypeScript

When converting a module that passes plain objects around, dump one of those objects to JSON, generate the interface, and use it as the parameter type — a far quicker route than annotating each property from memory.

Spotting an Unannounced API Change

Generate an interface from last month's saved response and from today's, then compare the two. New or renamed fields show up as differences in the type before they show up as a bug in production.