🛠️ JSON to TypeScript Converter

Last updated: May 1, 2026

JSON to TypeScript Converter

Paste any JSON sample — get clean TypeScript interfaces with nested types and optional fields instantly.

TypeScript Output
// Output will appear here...

JSON to TypeScript: Why Doing It Right Actually Matters

There's a certain kind of developer pain that comes from staring at a deeply nested JSON payload from some third-party API and thinking, "I'll just use any for now." And honestly, we've all been there. But "for now" has a way of becoming forever, and the moment you've got any scattered across your codebase, you've given up the one thing TypeScript is actually good at: catching mistakes before they bite you in production.

The gap between raw JSON and typed TypeScript interfaces isn't conceptually huge — but when you're dealing with a response that has fifteen nested objects and thirty fields, hand-writing it is tedious enough that most people skip it. That's the exact problem a JSON to TypeScript converter solves, and it's worth understanding what a good one actually does versus what a lazy one does.

What Gets Lost When You Type JSON by Hand

When you manually transcribe a JSON sample into TypeScript interfaces, a few things tend to go wrong. First, you miss fields. You're looking at one example response, you don't notice that metadata appears on some records but not others, and suddenly your interface says metadata: object when it should be metadata?: SomeShape. Second, you mistype. A field that comes back as a number gets typed as a string because you were skimming. Third — and this is the subtle one — you lose the nested structure. Instead of properly creating interface Order with its own interface LineItem, you collapse everything into a flat Record<string, any> and move on.

A proper converter handles all of this automatically. It walks every key recursively, infers primitive types from actual values, promotes array elements into their own named interfaces, and flags null values as optional — because in real APIs, null almost always means "sometimes this field isn't there."

The Optional Field Question: null vs. undefined vs. Missing

This is where TypeScript newcomers often get confused, and where converters diverge in quality. In a JSON payload, a field can be absent entirely, explicitly null, or present with a real value. TypeScript distinguishes between field?: string (may be absent or undefined), field: string | null (always present but possibly null), and field: string (always there, never null).

Most JSON converters take the lazy path: they see null in the sample and emit field: null, which is technically correct but practically useless. A smarter approach marks those as field?: null or field: string | null depending on what the field semantics suggest. Even better, when you provide multiple array elements with slightly different shapes, a good converter merges them — so if one order has a discount field and another doesn't, the resulting interface marks discount as optional rather than pretending it's always there.

Array Types: Simple Cases and the Messy Ones

Most JSON has arrays of homogeneous objects — a list of users, a list of products. That's easy: inspect the element type, create an interface, emit User[]. But real-world APIs send heterogeneous arrays. An "events" array might contain objects shaped like { type: "click", x: number, y: number } and { type: "keypress", key: string }. A naive converter generates a single merged interface with all fields optional. A thoughtful one might emit a union type: (ClickEvent | KeypressEvent)[].

For a single-sample converter (which is what most tools are), you're working with what you've got. The right behavior is to merge all array element shapes, mark any field that doesn't appear in every element as optional, and name the resulting interface something logical based on the parent key — UserOrders, not just Orders or, worse, Interface1.

Naming: The Underrated Part of Code Generation

Generated code is only useful if it's readable. When a converter takes the JSON key user_profile and names the interface user_profile, that's a miss. TypeScript conventions are PascalCase for interfaces, and a converter worth using transforms user_profile to UserProfile, order-line-items to OrderLineItems, and so on. Nested types should carry context from their parent: if the root interface is ApiResponse, a nested object on the data key should be ApiResponseData, not just Data.

This matters more than it sounds. When you've got ten interfaces in a file, generic names like Address and Settings clash. Context-aware naming produces self-documenting code that doesn't require renaming after generation.

The export and readonly Decisions

Two small choices that reveal whether a converter was built by someone who actually uses TypeScript daily. Should interfaces be exported? Almost always yes — you're generating types to share across your codebase, not to keep private. Should fields be readonly? That depends entirely on usage. If you're modeling API response types that get passed around but never mutated, readonly adds a useful constraint. If you're generating types for a form state object, readonly is wrong. Offering both as a toggle is the right call rather than hardcoding either decision.

What a Converter Cannot Do (and What to Do Instead)

A converter works from samples, and samples lie. If your API returns a user object where role is "admin" in your test data but can actually be "viewer" | "editor" | "admin" in production, the converter will generate role: string. That's correct but not as tight as role: "admin" | "viewer" | "editor". String literal union types require domain knowledge the converter doesn't have.

The practical workflow: use the converter to get 80% of the way there in seconds, then manually tighten string fields to literal unions where you know the enumeration, add JSDoc comments for non-obvious fields, and potentially split merged interfaces into discriminated union types where applicable. Treat generated code as a starting scaffold, not final output — but "scaffold in seconds" beats "write by hand for twenty minutes" every time.

TypeScript Interfaces vs. Type Aliases for JSON Shapes

One thing worth knowing: for JSON data shapes, interface and type are largely interchangeable. Interfaces can be merged via declaration merging; type aliases cannot. For generated types from an API response, neither matters — you won't be merging declarations. The convention in most TypeScript codebases is to use interface for object shapes (which is what converters emit) and type for unions and intersections. If you see generated output using type User = { ... }, it's not wrong, just slightly less idiomatic for this use case.

Bottom line: a good JSON-to-TypeScript converter is a time machine. You drop in a payload, get back something that would have taken twenty minutes of careful typing, and you spend those twenty minutes on the actual logic instead. The quality of what it generates — correct optional fields, sensible names, clean nesting — is what separates a tool worth bookmarking from one you use once and forget.

FAQ

What happens if my JSON has null values?
When "Mark null fields as optional" is enabled, any field with a null value gets the optional modifier (?) in the generated interface. This reflects real-world API behavior where null typically means the field may be absent. You can toggle this option off if you want to preserve null as a strict type instead.
Can I convert a JSON array, not just a JSON object?
Yes. If you paste a JSON array, the converter inspects the element objects and merges their shapes to produce a single interface. Fields that appear in some elements but not others are marked optional. The root type becomes an array of that interface, e.g. Root[].
How are nested objects handled?
Each nested object gets its own named interface. The name is derived from the parent interface and the key name, converted to PascalCase — so a field called "address" inside a "User" interface produces a separate "UserAddress" interface. This keeps types composable and avoids deeply nested inline type definitions.
What does the "Root name" field do?
It sets the name of the top-level generated interface. If you type "ApiResponse", you get interface ApiResponse { ... } as the outermost type. Nested interfaces are named relative to this root, so changing the root name cascades through all nested interface names as well.
Is my JSON sent to a server?
No. The entire conversion runs in your browser using client-side JavaScript. No data leaves your machine, which makes it safe to paste internal API payloads, tokens, or any sensitive response bodies without worry.
What should I do after generating the interfaces?
Generated interfaces are a solid starting point, not final code. Review string fields where you know the actual enumeration and replace string with a literal union like 'admin' | 'viewer'. Add JSDoc comments for non-obvious fields, and if the API returns different shapes under the same key based on a discriminator, split the merged interface into a proper discriminated union type.