I've been relying on JSON→Zod generators to scaffold schemas from API responses,
and kept wondering — how much do they actually infer vs just dumping z.string()?
So I ran three tools on the same payload to find out.
Input (same for all):
{ "status": "active", "email": "a@b.com", "id": "a1b2c3d4-…",
"joined": "2026-01-01T00:00:00Z", "age": 30, "website": "https://a.com" }
json-to-zod
z.object({
status: z.string(), email: z.string(), id: z.string(),
joined: z.string(), age: z.number(), website: z.string(),
})
Zero format detection. Technically correct, validates nothing.
quicktype (-l typescript-zod)
z.object({
status: z.string(), email: z.string(), id: z.string(),
joined: z.coerce.date(), // caught the date
age: z.number(), website: z.string(),
})
1 out of 6. Better, but stops there.
TypeMorph (disclosure: side project I built — take with salt)
z.object({
status: z.enum(["active", "pending", "closed"]),
email: z.email(),
id: z.uuid(),
joined: z.iso.datetime(),
age: z.number().int().min(0).max(150),
website: z.url(),
})
The interesting design questions this raised for me:
-
Enums from a single sample → you get
z.enum(["active"])which rejects valid future values. You need multiple samples aggregated before enums are safe. Worth it? -
Name-based heuristics (
age → .int().min(0).max(150)) — useful shortcut or too opinionated? -
Open-vocab fields like
currencyorcountryshould stayz.string()even if they look like enums
Is anyone doing something smarter here? Curious what approach others take
when generating schemas from real API data.

























