Generating Types from JSON: TypeScript, Zod, Pydantic and Go
Hand-writing types for a 40-field API response is the kind of work that produces both boredom and bugs — the typo you make on field 27 will surface as a runtime error three weeks later. Generating the types from a real payload is faster and more accurate, provided you understand what inference can and cannot know. This guide covers both.
Open the JSON to Types Converter →
What the converter does
The JSON-to-types converter parses your JSON and infers a type model from it: nested objects become named types, arrays are examined element-by-element, and the result is emitted in your choice of four targets — TypeScript interfaces, Zod schemas (with z.infer type exports), Pydantic v2 models, or Go structs with JSON tags. Everything runs in the page, so pasting a production API response is safe.
How does it decide a field is optional?
By merging. When an array holds several objects, their shapes are combined into one type: a key present in every element is required, and a key present in only some becomes optional. From this input:
[{ "id": 1, "total": 42.5 },
{ "id": 2, "total": 18.0, "discount": 2.5 }]
you get discount?: number in TypeScript, .optional() in Zod, discount: float | None = None in Pydantic, and Discount *float64 `json:"discount,omitempty"` in Go. The corollary: the more representative samples your JSON contains, the better the types. A single element can't reveal which fields are sometimes absent.
How nested types get their names
Object types are named after the key they appear under, PascalCased and singularised — orders: [...] yields Order, address: {...} yields Address, and name collisions get numeric suffixes. The root type is whatever you put in the name box. Since Zod and Pydantic require definitions to exist before use, types are emitted children-first with the root last — valid as-is in all four targets.
Nulls, unions and numbers
Three inference rules worth knowing:
- Null handling: a field that is sometimes
nullbecomesstring | null(TS),.nullable()(Zod),str | None(Pydantic) or a pointer (Go). Note that optional ("key absent") and nullable ("key present, value null") are different things, and the generator keeps them distinct. - Mixed arrays become unions —
(string | number)[],z.union([...]),list[str | int]; Go, which has no unions, falls back toany. - Numbers: if every sample of a field is a whole number you get
z.number().int(), Pydanticintand Goint64; one decimal anywhere widens it to float. TypeScript is alwaysnumber.
The caveats: what inference cannot know
Generated types describe the sample you pasted, not the API's contract. Before trusting them:
- Absent ≠ impossible. A field your sample never shows won't be in the type; a field that happened to appear everywhere may still be optional in the real API. Check the docs for load-bearing fields.
- Strings stay strings. Inference can't know that
"2026-07-22T09:00:00Z"is a date or that"pending"is one of five enum values — tighten those by hand (z.enum([...]),Literal,time.Time). - Empty arrays and empty objects have no element information, so they come out as
unknown[]/list[Any]/[]any.
The workflow that works: generate, then spend two minutes tightening — it's the 40 field names and the optionality analysis you wanted automated, and those are exactly what you got.
Which output should you use?
TypeScript interfaces when you just need compile-time shapes. Zod when data crosses a trust boundary — an API response, user input, a webhook — because a Zod schema validates at runtime and yields the static type for free via z.infer. Pydantic is the Python equivalent, doing parsing, validation and serialisation in one model. Go structs come tagged for encoding/json with omitempty on optional fields. For JSON that fails to parse in the first place, start with the JSON validator.
Ready to try it? Open the JSON to Types Converter →