JSON to TypeScript Interface Generator
Paste any JSON object and get a fully-typed TypeScript interface — or a set
of nested interfaces — generated instantly in your browser. Handles nested objects, arrays,
optional properties, union types and null. Nothing is uploaded.
Last reviewed 2026-06-19.
How it works
- Parse — your JSON is parsed with the browser's native
JSON.parse. If it is not valid JSON, the error message is shown immediately. - Infer — the value tree is walked recursively. Each object becomes a named
interface. For arrays, the type is inferred from all elements so the resulting
type covers every item. Keys whose value is
null, or that are absent from some array elements, are marked optional with?. - Emit — interfaces are output deepest-first (child before parent) so the
file compiles top-to-bottom without forward references. Each interface is
exported, ready to paste into any.tsor.d.tsfile.
When properties become optional
Two conditions cause a property to receive the optional marker (?):
-
The value in the sample JSON is
null— the generator adds?and appends| nullto the type, because the real API may return either the value or nothing. -
The JSON contains an array of objects and a key appears in some elements but not
all — the key is added to the merged interface but marked
?, because not every record will have it.
If you know a field is always present and never null, simply remove the ? from
the generated output.
JSON → TypeScript type mapping
| JSON value | TypeScript type | Example JSON |
|---|---|---|
string | string | "Alice" |
number | number | 42 or 3.14 |
boolean | boolean | true or false |
null | T | null (+ optional ?) | null |
array of T | T[] | ["a","b"] |
array of mixed primitives | (string | number | boolean)[] | [1,"x",true] |
object | interface Name { … } | { "city": "NYC" } |
unknown / empty array | unknown[] | [] |
Interface naming
Interface names are derived from the JSON key name and converted to PascalCase — for
example, a key user_profile produces an interface called
UserProfile, and lineItems produces LineItems. The
root object always uses the name from the Root interface name field above
(default Root). Rename it to match your domain model — for example,
User, ApiResponse, or Product.
Privacy & security
All processing happens entirely inside your browser — no data is sent to any server, no cookies are set, and nothing is logged. It is safe to paste real API responses, including those with personal data, because the content never leaves your device. The tool also works offline once the page has loaded.
What TypeScript is, and why types help
TypeScript is a typed superset of JavaScript created at Microsoft by Anders Hejlsberg
(the designer of C# and Turbo Pascal) and first released in 2012. Every valid JavaScript program is
already valid TypeScript; what TypeScript adds is a static type layer that the compiler
checks before your code ever runs, then erases entirely — the browser still receives plain JavaScript.
The payoff is that a whole class of bugs is caught while you type: a misspelled property, a number used
where a string was expected, a field that might be undefined. Editors use the same type
information to power autocomplete, inline documentation and safe automated refactoring.
TypeScript uses structural typing ("duck typing"): two types are compatible if their shapes match, regardless of their names. An object is acceptable wherever an interface is expected as long as it has at least the required properties of the right types. That is why a single, accurate interface generated from a real API response is so valuable — it describes the exact shape your code can rely on.
Why generate types from JSON?
When you consume a JSON API, the response is just untyped data — your editor knows nothing about it, so
every response.user.name is a guess that only fails at runtime. Generating an interface
turns that response into a contract: the compiler now knows which fields exist and what
type each holds, autocompletes them as you type, and flags response.user.naem as an error
immediately. Doing this by hand for a deeply nested payload is tedious and easy to get wrong, which is
exactly the job this tool automates — it walks the JSON once and emits accurate, nested, exported
interfaces you can paste straight into your project.
interface vs type alias
TypeScript offers two ways to name an object shape: an interface and a type
alias. For plain object shapes they are largely interchangeable, and this generator emits
interface declarations because they read cleanly and can be extended and merged.
Reach for a type alias when you need something interfaces cannot express directly: unions
(type Status = "active" | "archived"), tuples, mapped types, or aliasing a primitive. A
common convention is "interfaces for the public shape of objects, type aliases for unions and utility
types" — but consistency within a codebase matters more than the exact choice.
The limits of inferring types from one sample
This is the most important thing to understand about any JSON-to-types tool, including this one:
an interface is inferred from the single example you paste, and one example cannot
reveal everything about an API. If a field is sometimes present and sometimes absent but your sample
happens to include it, the generator will mark it required. If a field is usually a number but
occasionally null, only a sample that contains the null will produce the
optional, nullable type. An empty array gives no element type at all, so it can only be typed as
unknown[]. And a string that is really an ISO date, an email or an enumerated value is simply
typed as string, because at the JSON level that is all it is. Treat generated types as an
excellent first draft to refine against the API's real documentation, not as a guaranteed complete
contract.
null, undefined and the optional ? mark
TypeScript draws a careful distinction that JavaScript blurs. null is an explicit "no
value"; undefined is "this was never set"; and the optional marker ? means the
property may be missing from the object entirely. These are not the same thing. When you enable
the recommended strictNullChecks compiler option, the type system forces you to handle each
case, which prevents the classic "cannot read property of undefined" crash. This generator adds
? (and appends | null) when a sampled value is null, and adds
? alone when a key is absent from some elements of an array of objects — a faithful
reflection of what the sample showed. If your domain knowledge says a field is always present, simply
delete the ? from the output.
any vs unknown
When a type genuinely cannot be determined, you have two escape hatches. any switches type
checking off for that value — anything is allowed, which quietly reopens the door to the very
bugs TypeScript exists to catch. unknown is the safe alternative: it accepts any value but
forces you to narrow it with a check before you use it. Modern style strongly prefers unknown
over any, and that is why this tool emits unknown[] for an empty array rather
than any[] — it keeps your code honest until you supply a real element type.
Types vanish at runtime — validate untrusted data
Because TypeScript types are erased during compilation, they offer no protection at runtime. If a server returns data that does not match your interface — a missing field, a string where you expected a number — TypeScript cannot stop it, because by then the types no longer exist. For data crossing a trust boundary (an external API, user input, a webhook) you need a runtime validator. Libraries such as Zod, io-ts and Valibot let you declare a schema once and both validate incoming data and infer the matching TypeScript type from it, so the static type and the runtime check can never drift apart. JSON Schema with a validator like Ajv does the same job in a language-neutral way. The interfaces this tool produces describe the happy-path shape; a runtime validator guarantees the data actually arrived in it.
Sample-based vs schema-based generation
Generating types from a sample, as here, is the fastest path when all you have is an example response — no setup, no build step, paste and go. When an API publishes a formal description you can do better: an OpenAPI (Swagger) document or a GraphQL schema can be fed to a code generator that produces types covering every endpoint and every variant the API documents, and that can be re-run whenever the API changes so the types never go stale. Think of sample-based generation as the quick, ad-hoc tool and schema-based generation as the maintained, whole-API solution — they complement each other rather than compete.
From generated draft to production type
A few quick edits turn a generated interface into a polished domain type. Rename the root interface from
Root to something meaningful (User, ApiResponse,
Invoice). Tighten loose string fields into literal unions where the value set is
known ("draft" | "published"). Replace unknown[] with the real element type once
you know it. Consider "branding" identifiers so a UserId and an OrderId cannot be
mixed up even though both are strings. And if you are shipping a library, move the interfaces into a
.d.ts declaration file — a types-only file that describes shapes without
emitting any JavaScript — so consumers get full type information at zero runtime cost.
How arrays of objects become a single interface
When a JSON array contains objects, this tool does not generate a separate type for each element — it
merges them into one interface that describes the whole collection. It gathers every key
that appears across all the objects, infers each property's type from the values it sees, and marks any key
that is missing from at least one element as optional with ?. The property is then typed as an
array of that merged interface (for example users: User[]). This is usually what you want,
because a real API list is meant to hold items of one shape — but it also means a single odd element can
widen the inferred type, so it is worth pasting a representative sample rather than just the first record.
Enums and literal unions
JSON has no concept of an enum, so a field whose value is one of a fixed set — a status, a role, a country
code — arrives as a plain string, and that is how this tool types it. Tightening such fields by
hand is one of the highest-value edits you can make: change status: string to
status: "draft" | "published" | "archived" and the compiler will now catch any typo or invalid
value, while your editor autocompletes the allowed options. TypeScript also has a dedicated
enum keyword, but most modern codebases prefer literal-union types like the one
above, because they are simpler, erase cleanly to plain strings, and play nicely with JSON.
Utility types worth knowing
Once you have an accurate interface, TypeScript's built-in utility types let you reshape it
without rewriting it. Partial makes every property optional (handy for an "update" payload where
any field may be sent); Required does the reverse; Pick and Omit build
a smaller type from a few fields of a larger one; Readonly prevents mutation; and
Record describes a dictionary keyed by string. Generating the base interface here and then
deriving these variations from it keeps your types DRY — one source of truth, many shapes — so a change to
the underlying data shape only has to be made in one place.
A practical workflow
A reliable pattern is: call the real API once, copy a representative response, paste it here, and rename the
root interface to match the endpoint. Tighten obvious enum-like strings into literal unions, confirm the
optional markers against the API's documentation, and replace any unknown[] with the real
element type. For data that crosses a trust boundary, recreate the same shape as a Zod or JSON Schema
validator so the runtime check and the static type stay in sync. Done this way, a JSON sample becomes a
fully-typed, validated model in a couple of minutes instead of a tedious hand-translation.
Frequently asked questions
- Does this handle deeply nested objects?
- Yes. The generator walks the entire JSON tree recursively. Each nested object becomes its own named interface, and parent interfaces reference those child interfaces by name. Interfaces are emitted deepest-first so the output compiles top-to-bottom without forward references.
- What happens with arrays of mixed types?
- When all elements of an array are primitives (string, number, boolean) the generator produces a union array type such as (string | number)[]. When elements are objects it merges all observed keys across every element — a key present in some elements but not others is marked optional (?) so the type correctly represents all items in the array.
- What does the ? (optional) mark mean?
- A property marked with ? is optional — it may be absent from some objects in practice. The generator adds ? when a key's value is null in the sample JSON (indicating the real value may be absent), or when a key appears in some but not all elements of an array of objects. In TypeScript, an optional property may be missing from the object altogether — it is distinct from a property that is present but explicitly null.
- Can I use this output directly in TypeScript?
- Yes. The generated interfaces use standard TypeScript syntax and are exported, so you can paste them directly into a .ts or .d.ts file. You may want to rename interfaces or adjust optionality based on your domain knowledge — the generator infers types from a single sample JSON, so it cannot know which fields are truly required in every case.
- How do I handle arrays of objects?
- When an array contains objects the generator creates a named interface for the element type — for example a key users: [{...}] produces an interface Users {...} and types the property as users: Users[]. If different objects in the array have different keys, all keys are merged and any key missing from at least one element is marked optional.
- What if my JSON has keys that are not valid TypeScript identifiers?
- Keys that contain spaces, hyphens, dots, or start with a digit are not valid TypeScript identifiers. The generator detects these and wraps them in quotes — for example "my-key"?: string — which is valid TypeScript object type syntax. You can then access such properties with bracket notation: obj["my-key"].
- Can it output a Zod schema or a type alias instead of an interface?
- Yes. Use the Output selector above the tool to switch the target to a TypeScript type alias or a Zod v3 schema. The Zod output is built from the same inferred shape — strings become z.string(), numbers z.number(), nested objects z.object({...}), arrays z.array(...) and optional keys get .optional() — and it includes the import { z } from "zod" line so it is ready to paste. Reach for Zod when you need runtime validation, not just compile-time types.
Related tools
See all browser tools → · All developer & data conversions →