🧬 JSON → Type Definition Converter
Paste JSON and instantly generate TypeScript, PHP, Python, Go, and Rust type definitions. Handles nested objects and arrays.
💡 Tips
• Nested objects are automatically extracted as separate types.
• Array element types are inferred. Empty arrays become any[] / []interface{} etc.
• null is treated as optional / nullable like string | null.
• Change the root type name from the input above.
🔗 Related Tools
📖 Where people get stuck
Paste JSON and it generates type definitions for TypeScript, PHP, Python, Go, Rust, Kotlin and Swift, extracting nested objects as separate types and inferring element types from arrays. Everything runs in the browser. What can be inferred is only the shape present in the sample you provided — a single API response is what came back that time, not the specification. Treat the output as a starting point for hand-editing rather than as a finished artefact.
| Case | What happens | What to do |
|---|---|---|
| A type built from one sample does not hold in production | Inference works on the assumption that what it saw is all there is. A field absent from the sample therefore does not exist in the type at all, and a field that happened to carry a value is treated as required. Real APIs return different shapes when a search matches nothing, when an error occurs, when permissions are insufficient, and for older records. Enum-like fields are the sharpest case: if the sample only ever showed "active", the type becomes the literal "active", and the moment "archived" arrives the type is a lie. |
Run several responses through it. Take at least four cases — a normal one, an empty one, an error and a boundary — and compare the types produced; which fields are genuinely required becomes visible. If an OpenAPI specification exists, generate from that instead: openapi-typescript and oapi-codegen work from a description of every shape that can be returned, which is a different level of reliability from inference over samples. And always read what was generated — a type where every field is required and an enum is a single literal is a sign that you fed it one sample. |
| Empty arrays and nulls determine nothing | From "tags": [] there is no way to know the element type; the result is an anything-goes type such as any[], []interface{} or List<Any>, and the benefit of type checking disappears entirely. null is equally uninformative — whether "deleted_at": null is always null or null only this once cannot be told from one sample. More fundamentally, JSON distinguishes a missing key from a key whose value is null, and many type systems blur that distinction — in TypeScript, ?: and | null are not the same thing. |
When a response contains an empty array, run another response that has elements in it — a single element is enough to fix the type. If you cannot get one, write that array type by hand: think of the output as automating nine tenths and filling in the rest, and the work stops feeling like a burden. For null, only the API documentation can tell you — where an API distinguishes a missing key from a null one, the accurate TypeScript is both at once, as in deleted_at?: string | null. When in doubt, err loose: a loose type checked at runtime is safer than a strict type that throws at runtime. |
| The numeric type means different things per language | JSON has exactly one numeric type — no distinction between integer and decimal, no precision. The generator therefore has to choose something per language: float64 in Go, i64 or f64 in Rust, number in TypeScript. The dangerous case is identifiers: receive a large integer as float64 or number and past two to the fifty-third the precision is gone and the value is silently rewritten. 9007199254740993 becoming 9007199254740992 raises no error. Date-shaped strings, naturally, remain plain string. |
Always fix the types of identifiers, money and timestamps by hand after generating. Treating an identifier as a string is safest — there is no reason to hold a value you never do arithmetic on as a number, and indeed the Twitter and Discord APIs return large ids as strings. Never hold money in floating point: for the same reason 0.1 + 0.2 is not 0.3, use an integer in the smallest unit, or your language decimal type. Converting timestamps from string to a proper date type makes the type system force you to handle the time zone. Read the generated type line by line and ask, do I do arithmetic on this value? — if not, a string is fine. |
Do not treat a generated type as a trustworthy boundary. TypeScript types in particular exist only at compile time and verify nothing at runtime — write await res.json() as User and the compiler believes it is a User whatever actually came back. That is not a flaw in the type system but a design in which validating at the boundary is a different layer job. For data arriving from outside — API responses, form input — always add runtime validation such as zod or valibot; the type can be derived from the schema, so nothing is maintained twice. Go json.Unmarshal and Rust serde do validate and are safer, but by default they silently discard unknown fields — DisallowUnknownFields in Go and #[serde(deny_unknown_fields)] in Rust let you notice when the API changes.
📖 How to Use
-
1
Enter or paste JSONPaste JSON into the left input field. Use the Sample button to quickly load example data.
-
2
Select target languageChoose TypeScript, PHP, Python, Go, Rust, Kotlin, or Swift, and optionally change the root type name.
-
3
Copy and use the type definitionThe generated type definition appears on the right. Copy it to your clipboard and paste it into your code.
❓ Frequently Asked Questions
How are nested objects handled?
How are null fields typed?
Does it handle a top-level JSON array?
🐛 Found a bug or issue with this tool?
Free to use, no signup. Even just the steps to reproduce are helpful. Reports go directly to the operator and help us fix issues.
Thanks for your report!
Your report has been delivered to the operator and will be used to improve the tool.