JSON to TypeScript: Stop Writing Interfaces by Hand
Every frontend engineer has been there: a backend colleague pastes a 300-line JSON response into Slack and says "type this." You open your editor, start mapping string, number, and boolean by hand, and twenty minutes later you have a brittle interface that will drift the moment the API evolves. There is a better way — and it starts here.
What Problem Does This Tool Solve?
REST and GraphQL APIs return JSON. TypeScript applications need typed interfaces. Bridging that gap manually is error-prone and expensive:
- Nested objects require recursive interface definitions that are tedious to trace by eye.
- Optional vs required fields depend on domain knowledge the JSON sample may not convey.
- Union types (
string | null,number | string) appear in dynamic APIs but are invisible in a naivetypeofpass. - Array inference — knowing whether
items: []should beitems: unknown[]oritems: Product[]— requires reading the surrounding structure, not just the key name.
The JSON to TypeScript online converter handles all of this in milliseconds, entirely in the browser. No data leaves your machine.
How the Conversion Algorithm Works
The converter performs a single-pass recursive descent over the parsed JSON AST:
- Primitive detection —
typeof valuemapsstring,number,boolean,null, andundefinedto their TypeScript equivalents.nullis emitted asnullrather thananyto preserve nullability semantics. - Object materialisation — Each key-value pair becomes an interface property. Keys are sanitised (spaces and hyphens become camelCase, numeric-only keys gain an underscore prefix) to produce valid TypeScript identifiers.
- Array homogeneity check — If every element in an array shares the same resolved type, the output is
ElementType[]. If elements are mixed (e.g.,[1, "two", null]), a union type(number | string | null)[]is generated. - Recursive interface extraction — Nested objects are promoted to named child interfaces and referenced by name, keeping the root interface shallow and readable. The naming heuristic capitalises the property key: a key
userProfileproducesinterface UserProfile { … }. - Duplicate deduplication — If two sibling properties resolve to structurally identical interfaces, only one definition is emitted and both properties reference it.
The result is idiomatic TypeScript — not a one-liner type Foo = Record<string, unknown>, but properly annotated, deeply nested, named interfaces your IDE can navigate.
Step-by-Step Usage Guide
Step 1 — Paste your JSON
Copy the raw JSON from your API explorer, Postman response, or browser Network tab and paste it into the left panel. The converter accepts any valid JSON, including arrays at the root level ([{ … }, { … }]).
Step 2 — Review the output
The right panel updates in real time. Each interface is named after the key that produced it, with the root interface called Root. Scan for any types — they indicate keys the converter could not infer (e.g., an empty array []). You may want to annotate those manually once you know the element type.
Step 3 — Rename and adjust
Copy the output and rename Root to match your domain model (ApiResponse, UserProfile, CartItem). Add export keywords where needed. Consider moving nested interfaces to a shared types/ file.
Step 4 — Validate with tsc
Run tsc --noEmit against a file that imports your new types. TypeScript will surface any mismatches between your interface and the actual usage.
Engineering Concepts: TypeScript's Structural Type System
TypeScript uses a structural (duck-typed) type system rather than a nominal one. This means that two interfaces with identical shapes are assignable to each other even without an extends relationship. This has a direct consequence for generated interfaces: the output does not need to mirror your class hierarchy — it only needs to correctly describe the shape.
When working with API types, it is common to generate interfaces and then narrow them at runtime with type guards:
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
typeof (value as User).id === 'number'
);
}
Type guards bridge the gap between unknown (the safe type for unvalidated API data) and your generated interface. Libraries like zod and io-ts take this further by deriving both the runtime validator and the TypeScript type from a single schema definition.
Best Practices for Generated TypeScript Interfaces
Mark optional fields explicitly. If an API field is sometimes absent, add ? to the property:
interface Product {
id: number;
discountCode?: string; // absent for full-price items
}
Prefer unknown over any for dynamic payloads. Unlike any, unknown forces a type check before use:
const raw: unknown = JSON.parse(responseText);
const data = raw as ApiResponse; // explicit cast, auditable
Namespace your generated types. Group API response interfaces in a namespace or a dedicated file (api.d.ts) to prevent name collisions with your domain model.
Regenerate on schema change, not on every deploy. Generated interfaces are source artifacts — commit them to version control and regenerate them only when the API contract changes, not on every CI run. This gives you a meaningful diff when APIs evolve.
Version your interfaces. When an API ships a breaking change, add a version suffix (UserV2) and maintain both until all consumers are migrated.
Comparison: Manual vs. Tool-Assisted Typing
| Approach | Time for 50-field response | Error rate | Handles nesting |
|---|---|---|---|
| Manual | 25–40 min | High (typos, missed nulls) | Tedious |
| IDE plugin (Paste JSON as Code) | ~30 sec | Low | Yes |
| This online formatter | Instant | Low | Yes |
| zod schema derivation | Requires schema | Very low | Yes |
The online developer utility wins on speed and zero-setup. It is the right tool for one-off conversions, onboarding new API endpoints, and exploratory typing. For production systems with strict validation requirements, pair the generated interfaces with a schema-validation library.
Common Pitfalls to Avoid
- Trusting a single sample. A JSON sample is a snapshot. Run the converter against multiple response variations to catch fields that appear only under certain conditions.
- Ignoring date strings. JSON has no date type. The converter emits
stringfor ISO timestamps. Alias them:type ISODateString = stringand use that alias for clarity. - Forgetting discriminated unions. If your API returns
{ type: "order", … } | { type: "invoice", … }, the converter will produce a merged interface. Refactor the output into a proper discriminated union for exhaustive pattern matching.
This developer utility removes the mechanical work so you can focus on the semantic work — understanding what the data means, not transcribing its shape.