JSON to TypeScript Interface Generator
Turn a sample payload into TypeScript interfaces. Every nested object becomes its own named interface, and mixed arrays become union types.
The generator infers types from the values in your sample, which is the only information a single JSON document carries. There is no schema to read, so what you get out is a faithful description of what you put in, not a description of what the API can return. That distinction is the whole story of using generated types well.
For an API you are integrating against, paste the fattest response you can find, ideally one where optional fields are populated and arrays are non empty. Then treat the output as a first draft: rename the root, widen fields you know can be absent, and replace inferred literals with the domain types you actually want. It saves the mechanical typing, not the thinking.
What the inference can and cannot know
Optionality is inferred only from arrays. If you paste an array of objects and a key is missing from some elements, that key is marked optional with a question mark. If you paste a single object, every key it has is required and every key it lacks does not exist, because a single sample offers no evidence either way. This is why an array of two or three representative records produces much better types than one object.
A null value produces the type null, not a nullable version of some other type. There is no way to tell from null alone whether the field is a nullable string or a nullable number, so the generator reports exactly what it saw. You almost always want to widen these by hand to string | null or whatever the field really is. The same reasoning applies to an empty array, which becomes any[] because its element type is unobservable.
Nested objects are extracted into their own interfaces named after the key that held them, converted to PascalCase. A key named nested yields interface Nested, and the parent refers to it by name rather than inlining the shape. Two structurally identical objects under different keys therefore produce two interfaces rather than one shared one, so deduplicating them is a manual step if you care about it.
How JSON values map to TypeScript types
Every row is a real inference this tool performs. The last three are the ones that need your attention after copying.
| JSON value | Inferred type | Notes |
|---|---|---|
42 | number | Straightforward. All JSON numbers become number, integer or not. |
true | boolean | Straightforward. |
{"meta":{"city":"Seoul"}} | meta: Meta | A nested object is lifted into its own interface, named from the key in PascalCase. |
[1, "a"] | (number | string)[] | A mixed array becomes a union of the element types it observed. |
null | null | Not a nullable string or number. Widen this by hand once you know the real type. |
[] | any[] | The element type is unobservable from an empty array, so it falls back to any. |
[{"a":1},{"a":1,"b":2}] | b?: number | Only arrays reveal optionality. A key missing from some elements gets a question mark. |
Generating interfaces from an array of records
Two records where the second omits a key. That omission is what makes the field optional.
[
{ "id": 1, "name": "Ada", "nickname": "ada",
"tags": ["admin"], "meta": { "city": "Seoul" },
"deleted": null, "extras": [] },
{ "id": 2, "name": "Alan",
"tags": ["dev", "ops"], "meta": { "city": "Busan" },
"deleted": null, "extras": [] }
]interface RootObject {
id: number;
name: string;
nickname?: string;
tags: string[];
meta: Meta;
deleted: null;
extras: any[];
}
interface Meta {
city: string;
}The root interface is always named RootObject, and when the input is an array it describes the shape of one element rather than the array itself. Because nickname is absent from the second record, it is optional. Had you pasted only the first record, it would have been required.
Common Pitfalls
A single sample makes every field required
Optionality is derived by comparing objects within an array. Paste one object and you get an interface where nothing is optional, which will typecheck against your sample and then fail at runtime the first time the API omits a field. Paste an array with at least one sparse record, or add the question marks yourself.
{"a":1} -> a: number
[{"a":1},{}] -> a?: numberThe root interface is called RootObject
The generated name is RootObject, not Root and not something derived from your data. Rename it after copying to match your domain, and remember to update the references from any nested interfaces if you also rename those.
Generated types describe your sample, not the API contract
Inference cannot see fields your sample happened not to include, cannot know that a string field is really an enum of four values, and cannot know that a number is always an integer. If the API publishes an OpenAPI or JSON Schema document, generate from that instead. Use this tool when no schema exists.
How to Use
- Paste your JSON: Paste a response with every field populated. A sparse sample produces types that are too narrow.
- Click Convert: The tool analyzes your JSON and generates TypeScript interfaces automatically.
- Copy to your project: Copy the generated interfaces and paste them into your TypeScript project.
Key Features
- Automatic type inference from JSON values (string, number, boolean, null, arrays, objects)
- Nested interface generation for complex object structures
- Array type detection with union types when elements differ
- Optional property detection for keys that may be null
Use Cases
- Generating types for REST API response payloads
- Creating interfaces from JSON configuration files
- Bootstrapping TypeScript types from sample database records
Frequently Asked Questions
Are the generated interfaces accurate?
They accurately describe the sample you pasted. Whether that matches the API contract depends on how representative your sample is. Paste an array of several records with optional fields both present and absent for the best result.
Can I customize the generated interface names?
The root interface is named RootObject. Rename it after copying into your project, along with any nested interface names you want to change.
Does it handle nested objects?
Yes. Each nested object gets its own interface, and the parent interface references it by name. Deeply nested structures are fully supported.
What about arrays with mixed types?
They become a union of the observed element types. An array holding strings and numbers is typed (string | number)[]. An empty array becomes any[], because there is nothing to infer an element type from.
Why is my field typed null instead of optional?
A null value only tells the generator that null occurred, not what the field holds when it is populated. You get the type null, which you should widen by hand to something like string | null. Optionality is a separate signal and comes only from keys missing across array elements.
Does it deduplicate identical nested shapes?
No. Each nested object gets an interface named after its key, so two structurally identical objects under different keys produce two interfaces. Merge them by hand if you want a single shared type.
Should I use generated types or a schema?
Prefer a schema when one exists. Generating from OpenAPI or JSON Schema captures optionality, enums, formats, and constraints that no sample can reveal. Use this tool when the API has no published schema and a sample response is all you have.