About JSON to TypeScript
Array elements are merged rather than sampled: a key absent from any element comes out optional, and conflicting value types become a union with null sorted last. Identically shaped objects share one interface instead of generating duplicates, and property names that are not valid identifiers are quoted rather than mangled.
Hand-writing types for an API response is the chore this removes, and the mechanical parts are exactly the parts people get wrong by hand: which fields are optional, which values can be null, what shape the third level of nesting actually has. The generator walks the payload once and emits an interface per object shape — nested objects become named interfaces, arrays of objects are merged element by element, and two structurally identical objects share a single interface rather than producing near-duplicate declarations to maintain separately.
The merging rules are where the value lives. Across an array's elements, a key that any element lacks is emitted optional, and a key whose values disagree in type becomes a union — so a list where last_login is sometimes a string and sometimes null comes out as string | null rather than whichever the first element happened to show. Empty arrays are typed unknown[] instead of guessed at, empty objects become Record<string, unknown>, and keys like content-type that are not legal identifiers are quoted, not silently renamed.
One limitation is structural and worth planning around: the types describe your sample, not the API's contract. A field present in every element of a short sample is emitted required even if the producer considers it optional; a genuinely required field can look optional if your sample includes a malformed row. Generated types are the fast, accurate first draft — the producer's documentation is the authority on what is actually guaranteed, and the fields worth double-checking are precisely the optional ones.