Generate TypeScript types from JSON
A JSON-to-TypeScript generator is useful when an API response already exists and you need a first model fast. JSON All-in-One can draft TypeScript interfaces or classes from an observed JSON sample, inside the same local workspace where you inspect the data.
Treat the output as a review draft. TypeScript describes what your code expects after parsing; it does not prove that a future response is valid, and it does not replace runtime validation at a boundary.
Use a response with varied values
This response includes a present-but-null next_cursor, varied profile data, date-like strings, nested arrays, and large account IDs. It shows nullability, but it does not show an omitted cursor or tell us what a non-null cursor contains. Add more response samples before deciding those types.
If your API has several examples, paste an array or response object that includes the variation you care about. Missing fields and explicit null are different signals, and the generated TypeScript should make that difference visible.
{
"page": 1,
"next_cursor": null,
"items": [
{
"id": "usr_01",
"account_id": 9007199254740993,
"created_at": "2026-09-04T11:32:00Z",
"profile": { "display_name": "Ada", "roles": ["owner", "developer"] }
},
{
"id": "usr_02",
"account_id": 9007199254740995,
"created_at": "2026-09-04T11:40:00Z",
"profile": null
}
]
}Generate interfaces or classes
Open the JSON, choose Codegen, select TypeScript, and then choose Interface or Class. Interfaces are usually the lightest fit for API shapes. Classes can be useful when your project wants named constructors or instance methods later, but the generator should not be treated as a full runtime decoder.
The reviewed model below keeps the original JSON key names. If you change created_at to createdAt in a type, the incoming object does not change with it: you also need mapping code. next_cursor is shown as null because that is the only value in this sample; a wider API contract needs more evidence.
export interface ApiResponse {
page: number;
next_cursor: null;
items: Item[];
}
export interface Item {
id: string;
account_id: number;
created_at: string;
profile: Profile | null;
}
export interface Profile {
display_name: string;
roles: string[];
}Review the type semantics
profile: Profile | null means the key was observed with a null value. An optional property would mean the key was not present in every observed object. Those are separate cases in application code: one asks you to check the value, the other asks whether the member exists.
Date-like strings default to string for TypeScript. You can choose a native date policy, but that only changes the type to Date; it does not parse incoming JSON into Date objects by itself. Large integers are another review point. If a JSON token is used as an identifier, consider string, bigint, or BigNumber policy instead of letting it drift through ordinary number arithmetic.
| Observed JSON | Typical TypeScript question | Careful decision |
|---|---|---|
| "created_at": "2026-09-04T11:32:00Z" | Is this a date? | Keep string unless code actually converts to Date. |
| "profile": null | Nullable or optional? | Model as nullable when the key is present with null. |
| 9007199254740993 | Safe as number? | Use a string/big-number policy for identifiers. |
| ["owner", "developer"] | Enum or string array? | Enable enums only when the set is intentionally closed. |
Use the types in a project
The built-in TypeScript declaration for JSON.parse returns any. Assigning it to unknown makes the trust boundary explicit. The assertion below is only a usage sketch for a known fixture, not runtime validation or a recommendation to trust arbitrary responses.
The sketch reads names and date strings only. Ordinary JSON.parse rounds the large numeric account_id values in this fixture. For real ID handling, use a lossless loader or have the API send IDs as strings and update the model accordingly. A type assertion cannot recover digits lost during parsing; see JSON number precision.
import type { ApiResponse } from './ApiResponse';
declare const responseText: string;
// Known-fixture sketch only: this parser does not preserve large account IDs.
const raw: unknown = JSON.parse(responseText);
const data = raw as ApiResponse;
for (const item of data.items) {
const label = item.profile?.display_name ?? item.id;
console.log(`${label}: ${item.created_at}`);
}Workflow checklist
- Include multiple sample records when the real API returns arrays.
- Generate TypeScript and compare interface versus class output.
- Review
null, missing fields, date-like strings, enums, and large numbers. - Rename files or root models only after checking imports.
- Compile the generated files in your project TypeScript version before relying on them.
What the generated types cannot do
The TypeScript target produces model code, not runtime validators and not a complete SDK. If your project needs Zod validators or another runtime validation layer, add that layer separately and wire it to the same JSON fixture.
A careful handoff records the TypeScript version used for tsc and any manual edits made after generation. If your generated code uses BigNumber, bigint, or Date, make sure the surrounding project code actually creates those values rather than assuming JSON parsing will do it.