Generated types are a description of the samples you gave the generator, not a promise about the API. They are still the fastest way to a correct first draft, as long as you know where they lie.
Optional is not nullable
Case
In the JSON
TypeScript
Optional
The key is sometimes absent
name?: string
Nullable
The key is present with null
name: string | null
Both
Absent in some, null in others
name?: string | null
A single sample can show neither. That is why the tool asks for several samples and warns when you give it one: every field of a one-sample type is marked required.
Pick samples that disagree
A record with every field filled, and one with as many as possible empty.
A list with items and the same list empty (an empty array has no item type).
An error response, if your code receives them on the same path.
Types vanish at runtime: validate at the edge
TypeScript types are erased when the code runs, so a response that no longer matches fails somewhere far from the cause. The Zod output gives you a schema to parse the response with, and an inferred type from the same definition, so the check and the type cannot drift apart.
What the tool guesses, and flags
Strings that are always ISO dates, UUIDs, emails or URLs are annotated; one odd value turns the annotation off.
An object with many ID-like keys becomes a map instead of one field per ID.
Fields that hold more than one type are listed so you can check they are intended.
Whole numbers stay integers until a decimal shows up. A price that happens to be 10 in your sample may be 10.5 tomorrow.
Using the generator
Paste the samples into the tool or run node cli.mjs a.json b.json --style zod, read the notes it prints, and commit the result. Re-run it when the API changes.