How to Convert JSON to TypeScript Interfaces
Last updated:
A JSON-to-TypeScript converter infers interface declarations from a JSON sample in under 1 second — no manual field-by-field typing. Given {"name": "Alice", "age": 30}, the generator produces interface Root { name: string; age: number; }. Nested objects become nested interfaces, arrays become typed arrays like Item[], and nullable values automatically get | null. For a 30-field response with 5 nesting levels, automated generation saves 20+ minutes of mechanical work that quickly drifts out of sync. This guide covers how generators infer types, 3 tools to choose from (quicktype, json-to-ts, VS Code extension), handling optional vs required fields, nullable vs optional distinctions, and wiring the generated interfaces into a TypeScript project with runtime validation.
The problem with manual interface writing
When you get a new API response, the typical workflow is: look at the JSON, mentally map each field to a TypeScript type, write the interface, realize you missed a nested object, go back and fix it. For a response with 30 fields and 5 levels of nesting, that's 20 minutes of mechanical work before you can write a single line of actual logic.
Manual interfaces also drift from reality. The API adds a field in a new version, you forget to update the interface, and TypeScript silently passes while your runtime code breaks. Generating from an actual payload keeps the types grounded in what the API actually returns.
Basic JSON to TypeScript example
Start with a simple API response:
{
"id": 42,
"username": "alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z",
"active": true
}A generator produces:
interface Root {
id: number;
username: string;
email: string;
createdAt: string;
active: boolean;
}Straightforward. The interesting cases are where the naive approach fails.
Nested objects
Most real API responses have nested objects. A good generator creates separate interfaces for each nested level rather than inlining everything as any.
{
"user": {
"id": 1,
"name": "Alice"
},
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "62701"
}
}Correct output:
interface Root {
user: User;
address: Address;
}
interface User {
id: number;
name: string;
}
interface Address {
street: string;
city: string;
zip: string;
}Arrays and union types
Arrays of a single type are straightforward: string[], number[]. The tricky case is mixed-type arrays.
{
"tags": ["active", "premium"],
"scores": [98, 87, 92],
"mixed": [1, "two", true]
}Expected output:
interface Root {
tags: string[];
scores: number[];
mixed: (number | string | boolean)[];
}A naive generator might just produce any[] for mixed arrays. A better one inspects all elements and constructs a union type.
Null handling
This is where most hand-written interfaces go wrong. When a field is nullin the sample JSON, what type should it be?
{
"userId": 1,
"deletedAt": null,
"bio": null
}A generator that outputs null as the type is technically correct but useless:
// Bad — doesn't tell you what the field actually holds
interface Root {
userId: number;
deletedAt: null;
bio: null;
}A better approach marks nullable fields with a union:
// Better — acknowledges the field could have a real value
interface Root {
userId: number;
deletedAt: string | null;
bio: string | null;
}Since the generator can't know the intended type from a null value alone, string | nullis the reasonable default. You'll refine it based on the API docs.
Optional fields
Some generators produce optional fields (field?: Type) when a key is absent in some samples but present in others. This requires comparing multiple samples — something single-payload generators can't do. For a single input, all fields present in the payload should be required; you add ? manually where the API docs say the field is optional.
Arrays of objects
A common pattern: paginated list responses.
{
"total": 150,
"page": 1,
"items": [
{ "id": 1, "name": "Widget A", "price": 9.99 },
{ "id": 2, "name": "Widget B", "price": 14.99 }
]
}Expected output:
interface Root {
total: number;
page: number;
items: Item[];
}
interface Item {
id: number;
name: string;
price: number;
}The generator should inspect all objects in the array to build a unified interface. If one item has a field that another doesn't, the field should be marked optional.
What to look for in a JSON to TypeScript generator
- Creates named interfaces for nested objects (not inline types)
- Handles mixed-type arrays with proper union types
- Uses
string | null(notnull) for null values - Merges fields across array items to produce complete interfaces
- Runs in the browser — no server upload for sensitive API responses
- Handles deeply nested structures without hitting recursion limits
Try it now
Paste your JSON into Jsonic's JSON to TypeScript converter and get interfaces instantly. It handles nested objects, arrays, null fields, and union types. Everything runs in your browser — nothing is uploaded.
Open JSON → TypeScript ToolFrequently asked questions
How do I generate TypeScript interfaces from JSON automatically?
Paste your JSON into a generator tool. It infers a TypeScript type for each field and outputs named interfaces for nested objects. Jsonic's JSON to TypeScript converter runs entirely in your browser.
What TypeScript type is used for unknown JSON?
Use unknown — it forces you to narrow the type before accessing properties.JSON.parse returns any by default, which skips type checks. Cast to unknown and validate before use.
Should I use interface or type for JSON shapes?
Both work. interface is the default choice for object shapes and can be extended. type is better for union types liketype Status = 'active' | 'inactive'.
How do I handle optional fields in generated TypeScript interfaces?
Generators working from a single sample output all fields as required. Add ?manually for fields the API docs say are optional: bio?: string.
What is the difference between JSON and TypeScript enums?
JSON has no enum type — enum-like values are strings or numbers. In TypeScript, use a string literal union: type Role = 'admin' | 'editor'. Validate the parsed value matches a member before assigning it to the type.
How do I validate that runtime data matches my TypeScript interface?
TypeScript interfaces are erased at runtime. Use Zod, Valibot, or Ajv to validate that parsed JSON matches your interface. Zod's schema.parse(data) throws a detailed error if any field is wrong.
Recommended reading
- Effective TypeScript (2nd Edition) — Dan Vanderkam83 concrete ways to level up your TypeScript — inference, narrowing, and type-safe API boundaries.
- Designing Data-Intensive Applications (2nd Edition) — Martin Kleppmann & Chris RiccominiThe modern classic on data systems — encoding formats, schemas, replication, and stream processing.
As an Amazon Associate, Jsonic earns from qualifying purchases.