Last 12 weeks · 118 commits
4 of 6 standards met
Closes #2654 Summary t equality — fails if EventName2 is the buggy // (string undefined) intersection. util.assertEqual(true); + + // EventName2 must not include undefined. + util.assertEqual(true); expect(EventSchema.parse({ name: "x" })).toEqual({ name: "x" }); expect(EventSchema.parse({ name: ["x"] })).toEqual({ name: ["x"] }); ┊ review diff a/packages/zod/src/v3/tests/issue-2654.test.ts → b/packages/zod/src/v3/tests/issue-2654.test.ts @@ -4,23 +4,30 @@ import as z from "zod/v3"; import { util } from "../helpers/util.js"; test("issue #2654: union inside z.object should infer the same as standalone union", () => { +// Repro for issue #2654 +test("issue #2654: union type used inside z.object infers same as standalone", () => { const EventNameSchema = z.string().or(z.array(z.string())); type EventName = z.infer; const EventSchema = z.object({ name: z.string().or(z.array(z.string())), }); type EventWithName = z.infer; type EventName2 = EventWithName["name"]; + type EventName2 = z.infer["name"]; // Strict, invariant equality — fails if EventName2 is the buggy // (string undefined) intersection. + // Strict invariant equality — fails to compile if the inferred property + // type is the buggy + // intersection instead of . util.assertEqual(true); // EventName2 must not include undefined. + // The property type must not include undefined. util.assertEqual(true); + // Assignability is preserved with the fix: a value of either type works. + const a: EventName = "x"; + const a2: EventName2 = a; + const b: EventName = a2; + expect(b).toBe("x"); + expect(EventSchema.parse({ name: "x" })).toEqual({ name: "x" }); expect(EventSchema.parse({ name: ["x"] })).toEqual({ name: ["x"] }); + expect(EventSchema.parse({ name: ["x", "y"] })).toEqual({ name: ["x", "y"] }); }); ┊ review diff a/packages/zod/probe-2654.ts → b/packages/zod/probe-2654.ts @@ -0,0 +1,26 @@ +import as z from "zod/v3"; + +const EventNameSchema = z.string().or(z.array(z.string())); +type EventName = z.infer; + +const EventSchema = z.object({ + name: z.string().or(z.array(z.string())), +}); +type EventWithName = z.infer; +type EventName2 = EventWithName["name"]; + +// Bivariant equality +type Equals = + (() => T extends A ? 1 : 2) extends + (() => T extends B ? 1 : 2) + ? ((() => T extends B ? 1 : 2) extends (() => T extends A ? 1 : 2) + ? "equal" + : "subset-of-A-not-B") + : "not-equal"; + +type Result = Equals; +declare const r: Result; +// If the bug exists, Result will be "not-equal" or similar — this assignment +// will fail. If the types match, Result is "equal" and assignment succeeds. +const ok: "equal" = r; +console.log(ok); ┊ review diff a//home/azureuser/.cache/bounty-hunter/colinhacks_zod-2654/repo/packages/zod/probe-2654.ts → b//home/azureuser/.cache/bounty-hunter/colinhacks_zod-2654/repo/packages/zod/probe-2654.ts @@ -20,7 +20,6 @@ type Result = Equals; declare const r: Result; // If the bug exists, Result will be "not-equal" or similar — this assignment // will fail. If the types match, Result is "equal" and assignment succeeds. const ok: "equal" = r; +// FORCE FAIL: r should be "equal" but we expect "not-equal" to show the bug +const ok: "not-equal" = r; console.log(ok); ┊ review diff a/packages/zod/probe-2654.ts → b/packages/zod/probe-2654.ts @@ -9,17 +9,18 @@ type EventWithName = z.infer<typeof EventSchema
Fixes #6019. Problem When is replaced by a subclass — Zone.js's , Angular NgZone, , etc. — zod silently drops issues added inside an /, and resolves "successfully" before the async refinement runs: Reported in production via : a duplicate-email check silently let invalid submissions through. Cause A native promise returned from a user function is not the patched global (a subclass), so zod's checks treated the async result as a synchronous value. The native-ness then propagated up the parse chain — native yields native promises — so every downstream check failed too, and never awaited the refinement. Fix Add , which keeps the fast path and falls back to the tag. That tag is shared across promise subclasses and realms, while still excluding ordinary thenable objects: Every promise check in the parse pipeline (, transforms, codecs, objects, unions, records, pipes, /, …) now uses it. In an unpatched runtime the fast path matches exactly as before, so behavior is unchanged; only foreign/subclass promises that previously slipped through are now detected. Tests Added a regression test in that swaps for a subclass (restored in ) and asserts that an fails the parse and an resolves correctly. Verified red without the fix, green with it. Full v4 suite (2764 tests) passes. _Restores #6116, which closed automatically when my fork was briefly deleted. The fix commit is unchanged._
Summary Adds FullProduct.dev to the Zod Ecosystem section & page What is FullProduct.dev, how is it powered by Zod? It's a universal app starterkit built around using Zod as the single source of truth: Expands Zod schemas (v3 + v4 + mini) with powerful additional metadata and introspection API's Transform zod component prop schemas to Interactive MDX docs with Nextra Scaffold out an auto-generated graphql schema (+ queries) from your resolver's Zod input and output schemas Plugins to create DB models from Zod schemas to e.g. Mongoose Models hook to further keep things in sync Related V3 PR that got merged into V3 docs: #4131 How Zod is used as the core for Single Sources of Truth: https://fullproduct.dev/docs/single-sources-of-truth
Problem passed to silently drops the keyword: Fixes #6049. Root cause in runs after every processor and unconditionally deletes when the check returns . For a whose inner type is a containing a transform, returns — so the value that just set on the schema is immediately wiped. The deletion was intended for cases where is inherited from a pipe (output-side default), not for , which always declares an explicit input-side default. Fix Skip the step when . The processor always sets intentionally; removing it here is wrong regardless of whether the inner type transforms. Test changes Updated existing snapshot for (type-changing transform): with this fix, now appears in the input schema. The keyword in JSON Schema is advisory and does not need to validate against the schema type, so this is spec-compliant and more informative for tooling. Added a new test reproducing the exact case from #6049.
Repository: colinhacks/zod. Description: TypeScript-first schema validation with static type inference Stars: 43079, Forks: 2044. Primary language: TypeScript. Languages: TypeScript (89.3%), MDX (9.5%), HTML (0.7%), JavaScript (0.4%), CSS (0.1%). License: MIT. Homepage: https://zod.dev Topics: runtime-validation, schema-validation, static-types, type-inference, typescript. Latest release: v4.4.3 (1mo ago). Open PRs: 100, open issues: 128. Last activity: 2w ago. Community health: 85%. Top contributors: colinhacks, JacobWeisenburger, scotttrinh, jeremyBanks, igalklebanov, samchungy, tmcw, alexxander, noritaka1166, dependabot[bot] and others.