Types are inferred by merging, and that is where generators differ. Every value found at the same position, each element of an array and each sample of a field, is added into one description of that position. So [{"id":1,"email":"a@x.io"},{"id":2,"email":null,"admin":true}] gives one type in which email is string | null and admin is optional, admin?: boolean; a generator that reads only the first element leaves admin out, and one that reads only the second makes it required. A key whose values differ becomes a union, such as number | string, or string | Owner when it is an object in one sample and a string in another. An empty array is unknown[] unless another sample at the same position has elements, a key that is null in every sample is unknown, and an object with no keys is Record<string, unknown>, each with a warning, because the sample says nothing about the real type.
Each nested object becomes a named type, in PascalCase from its key: billing_address, billing-address and billingAddress all give BillingAddress. Array elements are named by a deliberately small rule, applied once per key: children and people become Child and Person, -ies becomes -y, -sses, -shes, -ches and -xes lose the -es, and any other final -s is dropped unless the word ends in -ss, -us or -is. A name the rule leaves unchanged gets Item added, so data gives DataItem. It is a rule, not a dictionary, and it gets some words wrong (movies gives Movy), so rename those. Two positions with structurally identical shapes share one declaration, so a user’s address and a company’s address are both Address; two different shapes that want the same name are numbered, Address and Address2, with a warning. Keys that are not identifiers, such as content-type or 2fa, are quoted.
JSON has one number type and no date type, and the output claims no more than the JSON does. Numbers are number, integers beyond 2^53 included, which JSON.parse rounds; those are named in a warning. Dates are string, because that is what JSON.parse returns, and a Date type would be a promise the runtime does not keep. The Zod output is for Zod 4: z.object, z.array, z.union, .nullable(), .optional() and a z.infer type for each schema, declared so no schema is used before it exists. Only there are strings checked for a format: where every sample of a field is an ISO 8601 date-time or date in exactly the form Zod accepts, it becomes z.iso.datetime(), with { offset: true } if any sample has an offset, or z.iso.date(). Readonly marks every property and array readonly, and in Zod adds .readonly(), which also freezes the parsed value. All of it describes the sample: a key present in every element you pasted comes out required, whatever the API documentation says.