Local JSON to TypeScript converter
One vendor’s 200-line API response, typed by hand, twice, because I miscounted a brace the first time. That was the morning I built this. Paste JSON, get TypeScript. Nested objects become their own named interfaces. Arrays come out typed, mixed ones turn into unions, and anything that’s null or just absent from your sample gets flagged optional. Rename the root, swap interface for a type alias, flip on readonly, decide what an empty array should be. Runs in your browser. Your JSON never goes anywhere.
Stays in your browser. And it’s guessing off a single sample, so give the unions and optional fields a real look before any of it ships.
What a JSON to TypeScript converter does for your project
External data hits your TypeScript app and the compiler immediately wants to know its shape. REST endpoint, webhook, a database row, some config file, a model handing you JSON back, doesn’t matter where it came from. The move I lean on: write an explicit type, then let the compiler scream the second a field gets quietly renamed or shows up as the wrong primitive. Typing that out by hand for a 200-line payload, though? That’s the part nobody enjoys, and you will fat-finger a key somewhere around line 80. So you let a converter read the shape off a sample and hand you something to edit, rather than a blank file and a long afternoon.
Paste the JSON. It parses right here in the browser, walks the tree, writes out clean declarations. Nested objects get lifted into their own named interfaces so the top-level type doesn’t balloon into a wall of fields. Arrays of one type come out as T[], and mixed ones land as unions like (string | number)[]. As for null in your sample: optional with ?:, a | null union, or kept as a literal null. Your call, it’s just a dropdown. Out the other end is plain TypeScript. Compiles in a throwaway Node script, compiles in a sprawling Next.js app, same either way.
How JSON to TypeScript inference works
Four moving parts, really. Your text goes through JSON.parse first. That’s two jobs at once: confirm the JSON actually parses, and get a live object graph in memory to crawl. Then it walks that graph depth-first and hangs a path off every value (root.users[0].profile.age, say), so the interfaces end up named after where they sit in the tree. Next, each value gets tagged with a TypeScript type. Could be a string or a number, an inline shape for objects, unknown for the empty arrays, a union it pieces together for mixed ones. Last step folds identical shapes together, so two array elements with matching fields share one interface instead of duplicating, then prints the file with tidy indentation, the readonly markers if you asked, and whichever of interface or type you picked.
- Validate the JSON: broken input gets you the exact line and column, so you’re not squinting at the whole blob hunting for one stray comma.
- Detect primitive types: every leaf lands on a TypeScript primitive, and numbers can be promoted to literal types when you want something tighter.
- Detect optional and nullable fields: a field that’s
nullin the sample, or present in some array items and missing from others, gets marked optional. You don’t have to ask. - Detect arrays and unions: objects with the same keys collapse into a single interface. Mixed primitives become a union. An empty array defaults to
unknown[]. - Render the output: top-level type up top, helper interfaces alphabetised underneath, and a trailing newline so your diffs stay quiet.
Common use cases for a JSON to TypeScript converter
- Wrapping a third-party REST API. A vendor mails you a sample response and you want the client typed today, not whenever the sprint allows. Paste it, grab the interface, drop it into
api.ts. Loosen the optional fields later, once you’ve actually sat down with their docs. - Mapping a database query. Your
SELECThands back a row shape, and this gives you the matching interface, so whatever the query function returns is fully typed rather than a silentany. - Validating a JSON config file. Half your toolchain (ESLint, Vite, take your pick) lives in JSON. Generate the interface and the build can enforce that shape, flagging a broken field before it ever runs.
- LLM structured output. Asking a model for JSON? The cleanest contract you can hand it is a TypeScript interface. Generate one from a solid sample, then paste it into the system prompt, or use it to seed a Zod schema.
- Webhook payloads. Stripe, GitHub, Slack, all of them publish example payloads. Turn one into an interface and your handler stops guessing. The shape it expects is sitting right there in the code.
- Migrating from JavaScript to TypeScript. Porting a Node service, the slog is typing every old data structure by hand. This makes it paste-and-edit instead of an afternoon spent squinting at field names.
- Sharing types between frontend and backend. One canonical JSON sample becomes a shared interface both sides import, and a whole genre of API-drift bug quietly stops happening to you.
Limitations and review tips
The catch with any tool that sees exactly one example: it can’t tell a genuinely required field from one that just happened to show up this time. Hand it "middleName": "Anne" and it’ll insist middleName is always a string, even though half your users don’t have one. So don’t trust the first pass. Feed it a couple more samples, or go read the docs, and hand-add ?: to anything you know can vanish. Literal types spring the same trap. "role": "admin" looks like an enum, but the API might hand back "manager" the next call. On a closed set, the literal-string option pays for itself. On free-form user input, honestly, it just gets underfoot.
Under the hood it’s JSON.parse and a small inference routine, running in your browser and nowhere else. Nothing’s uploaded or phoned home anywhere. So paste the production payloads, the customer records, the schema that hasn’t shipped yet. It stays on your machine. And if you want runtime checks and not only compile-time ones, back the generated interface with a schema library (Zod, Valibot, ArkType, io-ts), so whatever comes off the wire gets validated against the very shape the compiler is already holding you to.
Frequently asked questions
Why does the converter sometimes mark a field as optional?
Two reasons, really. The field came back null in your sample, or it turned up in some objects of an array and not the others. Either way I read that as “might not be there,” so it defaults to ?:. Want it required but still allowing null? Switch the option to T | null.
How does the converter handle arrays of mixed types?
All elements the same primitive? You get string[] or number[], whichever it is. Mix the primitives and it falls back to a union, something like (string | number)[]. And an array of objects that all share the same keys collapses into one generated interface, so you land on User[] rather than the same inline shape copy-pasted down the page.
What does the literal strings option do?
Off by default, so "role": "admin" widens to role: string. Flip it on and it stays narrow: role: "admin". Handy when the value comes from a fixed set. From there you widen it back by hand into a union, say "admin" | "editor" | "viewer".
Can I use the output with Zod or Valibot validators?
Yep. It’s plain TypeScript, so a Zod or Valibot or ArkType schema can mirror it without any fuss. What I usually do: keep the inferred interface as a reference, then write the Zod schema and let z.infer<typeof schema> regenerate the type. Both describe the same structure. They only drift apart if you go out of your way to make them.
Is the JSON sent to your server?
No. It’s 100% client-side. Your browser’s JSON.parse reads the text, the inference runs in memory, full stop. No fetch fires off. Nothing gets logged. There’s no analytics tag sitting there watching what you type. So payloads with secrets in them, customer data, a schema that hasn’t shipped yet, none of it leaves the tab.
Should I prefer interface or type for generated declarations?
Shape that might get extended or merged across declaration files? Reach for interface. Working with unions and mapped types and intersections? You’ll want type. The Style dropdown does either, and structurally the result is the same anyway, so honestly, I’d just match whatever the codebase already does and not lose sleep over it.
Sources & further reading
Related tools and resources
Typing JSON is one stop on a longer trip. These are the tabs I keep open next to this one, for validating and formatting and generally poking at payloads, before the types get generated and after.













