★ TypeScript · from JSON
JSON to TypeScript Converter
Paste an API response, get interfaces you can drop into your codebase. Every element of every array is read, so a key that only shows up sometimes comes out optional instead of silently required.
- Runs in your browser
- No signup
Convert to
Strictness
Your JSON
TypeScript
3 types · 16 fieldsexport interface Root {
id: number;
email: string; // email
"display-name": string;
createdAt: string; // date-time
plan: null;
tags: string[];
team: Team;
invoices: Invoice[];
}
export interface Invoice {
number: string;
amount: number;
paid: boolean;
paidAt: string | null; // date-time
note?: string;
}
export interface Team {
id: string; // uuid
name: string;
website: string; // url
}
Typing APIs by hand?
I set up end-to-end type safety (DB → API → UI) so this never happens again.
How the TypeScript mapping works
Each JSON value is read, merged with every other sample of the same key, and then mapped to the closest TypeScript type:
| In your JSON | TypeScript |
|---|---|
| string | string |
| integer or decimal number | number |
| true / false | boolean |
| null | null (or T | null when other samples have a value) |
| key missing in some array elements | key?: T |
| [] with items | T[] |
| [] empty | unknown[] |
| nested object | its own named interface |
| mixed values | union, e.g. number | string |
Worked example
A user record with a nested team, a list of invoices (one missing a note, one with a null payment date) and an awkward display-name key:
{
"id": 1042,
"email": "ada@example.com",
"display-name": "Ada",
"createdAt": "2026-11-07T09:30:00Z",
"plan": null,
"tags": ["beta", "founder"],
"team": {
"id": "7f3c9a2e-1b4d-4c8e-9f2a-6d5e4c3b2a19",
"name": "Analytical Engines",
"website": "https://example.com"
},
"invoices": [
{ "number": "INV-001", "amount": 49.5, "paid": true, "paidAt": "2026-10-01T00:00:00Z" },
{ "number": "INV-002", "amount": 49, "paid": false, "paidAt": null, "note": "Card declined" }
]
}Becomes this TypeScript, generated by the same engine that runs the converter above (3 types, 16 fields):
export interface Root {
id: number;
email: string; // email
"display-name": string;
createdAt: string; // date-time
plan: null;
tags: string[];
team: Team;
invoices: Invoice[];
}
export interface Invoice {
number: string;
amount: number;
paid: boolean;
paidAt: string | null; // date-time
note?: string;
}
export interface Team {
id: string; // uuid
name: string;
website: string; // url
}
TypeScript gotchas
Optional is not nullable
key?: string means the key can be absent. key: string | null means it is always there but can be null. APIs do both, and this tool keeps them apart.
Numbers are just number
TypeScript has no integer type. If you need to tell IDs from prices at runtime, generate Zod instead and keep .int().
Types are compile-time only
Interfaces vanish after compilation, so a bad API response still gets through. Validate at the boundary with Zod if the data isn't yours.
Questions developers actually ask
Should I use interface or type?
How do I handle keys with dashes or spaces?
Why is a field typed as null?
Is my JSON sent to a server?
How does it know a field is optional?
Other languages
More from the workbench
Fine print
Tools: on the house.
Engineer: by the hour.
A codebase that doesn't need this tool is the upgrade. I set up end-to-end type safety so your API, database and UI agree by construction.
I'm Himanshu, an AI and full-stack engineer. I build MVPs, AI agents and the growth plumbing around them for early-stage startups.
