Skip to main content

JSON to TypeScript

Turn any JSON payload into clean, ready-to-paste TypeScript interfaces

Free online JSON to TypeScript converter. Paste a JSON object or array and instantly get properly named, nested TypeScript interfaces with optional properties, unions for mixed arrays, and quoted keys for invalid identifiers. Everything runs in your browser — nothing is uploaded.

object, array or primitive

// Paste JSON on the left to generate TypeScript interfaces.

💡 Tip: properties missing from some objects in an array are marked optional with ?, mixed types become a union such as string | number, and keys that are not valid identifiers (like user-name) are automatically quoted — so the output always compiles.

💡 Tip: Paste with Ctrl+V and types regenerate on every keystroke. Press Ctrl+Enter to force a re-run.


How to convert JSON to TypeScript interfaces?

Paste your JSON and the types appear as you type — no configuration required:

  • Copy a JSON response from your API client, a config file, or a database dump and paste it into the input box on the left.
  • The TypeScript interfaces are generated automatically on every keystroke. There is no Convert button to press.
  • Set the root interface name if you want something other than 'Root' — for example 'ApiResponse' or 'User'.
  • Click Copy and paste the result straight into your project's .ts file.

Understanding the generated types

The generator infers structure directly from your data:

  • Strings become 'string', numbers become 'number' (integers and decimals alike), booleans become 'boolean', and JSON null becomes 'null'.
  • Nested objects become their own interface, named after the path that leads to them — 'Root' with an 'address' object yields 'RootAddress'.
  • Arrays of objects become 'InterfaceName[]' with all keys merged into a single interface, so [{a:1}, {a:2, b:3}] becomes one interface where 'b' is optional.
  • Arrays with mixed element types produce a union, for example '(string | number)[]'.
  • Keys that are not valid TypeScript identifiers — such as 'user-name' or '2fa' — are automatically quoted.

Optional properties and unions

Real API data is inconsistent, and the output reflects that honestly:

  • When the same key is missing from some objects in an array, it is marked optional with a '?' suffix rather than being silently treated as required.
  • Keys that appear in some elements but never in others still get generated, so you never lose a field that exists in your sample.
  • Mixed types for the same key become a union such as 'string | number', which is far safer than guessing a single type.
  • Sort keys, add 'export' and mark properties 'readonly' with the toolbar toggles if your codebase conventions require it.

Related Tools

You May Also Need

Why type your JSON at all?

TypeScript's whole value proposition is that the compiler knows the shape of your data before you run the program. The moment data crosses a boundary you do not control — an HTTP response, a webhook payload, a JSON file on disk, localStorage — that knowledge stops. The conventional quick fix is 'any', and it silently disables type checking for everything downstream: typos in property names, mismatches between a string and a number, and calls to methods that do not exist all compile happily and fail at runtime. Writing the interfaces by hand is tedious and error-prone for large payloads, and it is exactly the kind of mechanical work a generator should do. Converting the real response — not an imagined one — means the types you get are grounded in actual data, so fields you forgot about are never omitted. In practice this also serves as documentation: anyone reading the interface immediately understands what the API returns, which fields are guaranteed, and which are optional.

How type inference works under the hood

A JSON value maps to a surprisingly small set of TypeScript types. A string is 'string', a number is 'number' — and yes, JSON has no integer type, so a price of 9.99 and a count of 3 are both 'number'. A boolean is 'boolean', and JSON's null is 'null'. Objects and arrays are structural and need recursive handling. The generator walks the parsed value depth-first, keeping a naming context: each nested object gets an interface whose name is derived from the path of keys leading to it, which is why a 'user' object with a nested 'address' produces 'RootUserAddress' rather than a flat, ambiguous 'Address' that would collide with a different address elsewhere in the payload. Arrays are the interesting case. A JSON array is dynamically typed and its elements may have different shapes; the correct typed representation is the union of their types. When every element is an object, the keys of all elements are merged into a single interface, and any key absent from at least one element is marked optional. This models reality: the API usually returns the same object, and fields that are conditional on server state genuinely may be missing.

Optional properties and the honest-union principle

The single most common source of runtime crashes in typed codebases is a type that claims a property always exists when it sometimes does not — for example, an API returns the field for premium accounts only, and code compiles fine but throws on a free plan. Marking a property optional forces every read to be checked or narrowed. The generator marks a property optional whenever it is missing from at least one element of an array it inspected, and never invents a property that was not present. Similarly, when a key holds a string in one element and a number in another, emitting 'string | number' keeps the compiler honest, whereas picking a single type would be a lie that only surfaces as a production bug. The corollary is that the quality of your types depends on the quality of your sample: a payload where every object happens to include a field will produce a required property. The fix is to include the most complete response you can — one object with all fields populated, or several objects with different shapes — and regenerate.

Generating types from a real API response

The fastest workflow is to capture a real response and paste it here. In Chrome or Firefox, open DevTools → Network, click the request, choose the Response tab, and copy the JSON body. From a terminal, 'curl -s https://api.example.com/users | pbcopy' on macOS or 'curl -s ... | clip' on Windows does the same in one step. From Node, 'fetch(url).then(r => r.json()).then(d => require("fs").writeFileSync("out.json", JSON.stringify(d, null, 2)))' saves a pretty-printed copy. Paste that into the tool rather than a hand-written example: it is the only way to be sure you have typed the fields the server actually sends. If the response is an array with many elements, delete all but two or three — leaving one complete object and one sparse object is enough to get both required and optional properties right. Then check the result into your project and use it in a typed fetch wrapper, where the JSON.parse result is cast once at the boundary and every consumer benefit from compiler-checked field access.

Where the generated types fit in your codebase

Generated interfaces are most useful at the boundary of your application, not scattered through it. A common pattern is a single 'src/types/api.ts' file holding interfaces grouped by endpoint, plus one thin fetch helper that casts the parsed body once: 'const data = (await res.json()) as ApiResponse'. Note that a cast is a promise, not a check — TypeScript cannot verify a runtime JSON payload against a compile-time interface. For genuinely untrusted or external input, pair the generated interfaces with a runtime validator such as Zod, Valibot, or io-ts, which check the actual values as the data flows in. The interfaces this tool produces are a good starting point for building a schema: they already tell you which fields are required, which are optional, and which may be null or a union, which are exactly the decisions a validator makes explicit. A public UI component or form library can then consume validated data with full confidence, and your error handling moves from 'cannot read property of undefined' in production to a typed, catchable validation failure at the edge.

Frequently Asked Questions (FAQs)

Is my JSON data sent to a server?

No. Parsing, inference, and code generation all happen in your browser with plain JavaScript, and nothing is uploaded, logged, or stored. That means this tool is safe for internal API payloads, database rows, and configuration containing field names you would rather not ship to a third party. The trade-off of doing the work locally is that very large payloads are limited by your machine's memory rather than a server's — which in practice is far beyond anything you would paste by hand.

How does it name the generated interfaces?

Nested interfaces are named after the path that leads to them, starting from the root name you provide. With a root name of 'Root' and an 'address' object nested under 'user', the result is 'RootUser' and 'RootUserAddress'. This keeps names unique and unambiguous across the payload, unlike flat naming where two different 'Address' objects would collide. Two properties holding structurally identical objects share a single generated interface instead of duplicating it — for example 'home' and 'work' both referencing 'RootHome' — and if two different shapes would produce the same name, a numeric suffix is appended so the output always compiles.

Why is a property marked optional in the output?

Because it was missing from at least one object in an array you provided. When several objects are found at the same position, the generator merges all of their keys into one interface and marks any key that is absent from at least one element with a '?' suffix. This is deliberate: a property that the API sometimes omits must be optional in TypeScript, otherwise the compiler hides a real possibility of undefined at runtime.

What happens with an empty array or an empty object?

An empty array has no elements from which to infer a type, so it becomes 'unknown[]' — the type is not knowable from the data, and 'unknown' is the honest answer that forces a check before use. An empty object becomes 'Record<string, unknown>' for the same reason. Both are safe placeholders: refine them by pasting a payload that actually contains an element, or narrow them by hand once you know the real shape.

Does it generate more than interfaces, like Zod or runtime validators?

No — the output is plain TypeScript interfaces or type declarations. That is intentional, because interfaces are what most codebases want to check in and import. If you need runtime validation, the generated types are an excellent blueprint: every required versus optional decision and every union is already worked out, so translating them into a Zod schema, a Valibot schema, or an io-ts codec is mechanical. TypeScript types are erased at runtime and can never validate a payload by themselves.

Which JSON features are supported?

Objects, arrays, strings, numbers, booleans, and null, including deeply nested and mixed structures. Invalid TypeScript property names are automatically quoted, so keys like 'user-name', '2fa', or 'class' produce valid, compiling output. The tool also handles a payload that is a top-level array — in that case the root name is used for the element interface and the declared type becomes 'Root[]'. JSON that fails to parse shows a precise error message instead of producing broken types.

Should I commit generated types to my repository?

Yes, in most projects. Committing generated interfaces gives you reviewable diffs when an API changes — a new field or a newly optional property shows up in a pull request rather than surprising a developer at runtime. Regenerating on every build hides those changes and adds a build-time dependency on the API. The pragmatic approach is to treat the generated file like source code: regenerate when the API contract changes, commit it, and let code review catch the implications.

Recently Used Tools