JSON to TypeScript types

JSON sample to TypeScript interfaces, Zod, JSON Schema, Go structs or Python.

These options apply to TypeScript.

Output

4 named types · 28 lines

What this tool does

Paste a JSON sample, for example a response copied from the browser's network panel, and read the types it implies. The same inferred shape is written five ways: TypeScript interfaces or type aliases, a Zod schema with z.infer types, a JSON Schema (draft 2020-12), Go structs with json tags, and Python dataclasses. Nothing is uploaded; the JSON is parsed in your browser by the same strict parser as the JSON formatter, so a syntax error is reported with its line and column.

How the types are inferred

  • Objects become named types. The name comes from the key (customer → Customer, shipping-address → ShippingAddress); elements of an array are named in the singular (items → Item, categories → Category).
  • Arrays are merged into one element type. If the first order line has no gift_note and the second has one, the field becomes optional: gift_note?: string. Different kinds become a union, so [1, "a", null] is (string | number | null)[].
  • null stays in the type. A field that is null in one element and a string in another is string | null. A field that is only ever null is typed null, which is honest: the sample says nothing more.
  • Empty arrays are unknown[] until another sample shows what they hold. An empty object is Record<string, unknown>, not {}, because in TypeScript {} means "any value except null".
  • Integers and decimals are both number in TypeScript, but the difference is kept where a language cares: int64 or float64 in Go, int or float in Python, integer or number in JSON Schema, .int() in Zod.
  • Keys that are not identifiers (first-name, 2fa, an empty string) are quoted. In Go and Python they get a valid field name plus the original key in the tag or a comment.

Names that collide

Two objects with exactly the same fields share one type, however many times they appear. Two different objects that want the same name are numbered: a user with id and another user with name become User and User2, so rename the second one to something meaningful. Names that would clash with built-in types are suffixed: a key called date gives DateType, because interface Date would silently merge with JavaScript's own Date and add fields to every date in your program.

Options

  • interface or type alias. Interfaces can be extended and merged; type aliases can be combined with unions. For data shapes either works.
  • readonly marks every field and array as read-only, useful for state that must not be mutated.
  • Make every field optional when the sample is one record and you know other records leave fields out.
  • Nested types inline writes one large type instead of one per object.
  • camelCase renames user_id to userId. The names then no longer match the JSON, so only use it if your code converts the keys.

Limits worth knowing

A sample shows what one response contained, not every response the API can send. A field missing from all your samples is missing from the types; a status string is string, not a union of its allowed values; a date is a string. Paste several records as an array to merge them, then tighten the result by hand: turn status: string into "pending" | "paid" | "shipped". For values coming from the network, the Zod output also checks the data at run time instead of trusting it. The article generating TypeScript types from API JSON goes through a full example and why any creeps back in.

Frequently asked questions

Why is a field typed null instead of string | null?

Because every sample you pasted had null there, so nothing shows what it holds otherwise. Paste a record where it has a value, inside an array with the first one, and the two are merged into string | null.

How are arrays with different objects handled?

All elements are merged into one object type. Fields present in every element are required, fields present in only some become optional, and fields whose kinds differ become unions. This is what you want for lists of records; for tagged unions, split the type by hand afterwards.

Should I use the TypeScript types or the Zod schema?

Types disappear when the code is compiled, so they cannot catch an API that sends something different. A Zod schema checks the data when it arrives and gives you the same types through z.infer. Use types for data you control and a schema at the boundary.

Is my JSON uploaded anywhere?

No. Parsing and code generation run in your browser. You can paste production responses, but it is still good practice to remove tokens and personal data first.

Why does the key date become DateType?

An interface named Date would merge with the built-in Date type and change it everywhere in your program. Built-in names such as Date, String, Map, Record and Response get a Type suffix to avoid that.