-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Support recursive types in z.object()
, drop z.interface()
#4271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis update overhauls how optionality is tracked and represented in schema definitions across the codebase. The old approach, which used separate flags for input/output optionality, is replaced by a unified Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant SchemaFactory
participant ShapeMeta
participant Parser
User->>SchemaFactory: define object/interface schema
SchemaFactory->>ShapeMeta: process shape, assign opt ("required"/"optional"/"defaulted")
User->>Parser: parse input with schema
Parser->>ShapeMeta: check .optionality for each property
Parser-->>User: return parsed result with correct optionality
sequenceDiagram
participant Schema
participant JSONSchemaGen
Schema->>JSONSchemaGen: toJSONSchema({ io: "input" | "output" })
JSONSchemaGen->>Schema: read shape[key].optionality
JSONSchemaGen->>JSONSchemaGen: build required[] based on opt
JSONSchemaGen-->>Schema: return JSON Schema with correct required fields
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 15
🔭 Outside diff range comments (7)
packages/zod/tests/interface.test.ts (1)
159-177
:⚠️ Potential issueFailing assertion –
safeParse
now succeedsCI reports that line 173 (
expect(c.safeParse(aData).success).toEqual(false)
) is red.
Because the mergedc
schema inherits optionality froma.shape
➕b.shape
,aData
actually satisfiesc
, sosafeParse().success
comes backtrue
.Either:
- The behaviour is correct → update the expectation to
true
.- The behaviour should be “fail” → we need to strip
"b"
froma.shape
before merging.- expect(c.safeParse(aData).success).toEqual(false); + expect(c.safeParse(aData).success).toEqual(true);Double-check the intended semantics and adjust accordingly.
🧰 Tools
🪛 GitHub Check: Test with TypeScript 5.5 on Node latest
[failure] 173-173: tests/interface.test.ts > extend overrides with .shape
AssertionError: expected true to deeply equal false
- Expected
- Received
- false
- true
❯ tests/interface.test.ts:173:38
🪛 GitHub Check: Test with TypeScript latest on Node latest
[failure] 173-173: tests/interface.test.ts > extend overrides with .shape
AssertionError: expected true to deeply equal false
- Expected
- Received
- false
- true
❯ tests/interface.test.ts:173:38
🪛 GitHub Actions: test
[error] 173-173: AssertionError: expected true to deeply equal false in test 'extend overrides with .shape'
packages/core/src/util.ts (1)
770-786
: 🧹 Nitpick (assertive)
extendObjectLike()
double-copies shape – considerobjectShapeMeta
extendObjectLike()
merges two already-meta-fied shapes, but it doesn’t
pass the incomingb
’s raw additions throughobjectShapeMeta
.
If a caller mutatesb
’s shape after creation,extendObjectLike()
will
not reflect the updated optionality flags. Re-using the helper keeps things
DRY and future-proof:- const _shape = { ...a._zod.def.shape, ...b._zod.def.shape }; + const _shape = { + ...a._zod.def.shape, + ...b._zod.def.shape, + };becomes
const _shape = { ...a._zod.def.shape, ...util.objectShapeMeta( Object.fromEntries(Object.entries(b._zod.def.shape).map(([k, v]) => [k, v.type])) ), };Up to you whether the extra allocation is worth it.
packages/core/src/api.ts (1)
1016-1028
: 🧹 Nitpick (assertive)Commented-out
shapeMeta
blocks – remove or reviveThe new design drops
shapeMeta
, but leaving the commented code around
adds noise and invites confusion. If the plan is to re-introduce it, add a
TODO
comment with context; otherwise, nuke the dead code.packages/core/src/schemas.ts (1)
1516-1524
:⚠️ Potential issueDebug
console.log
sneaked inLooks like a stray
console.log({ optionalKeys })
slipped past the goalie. 🥅
Leaving debug logs in published code makes noise and can hurt performance in hot paths.- console.log({ optionalKeys });
Kill it with fire (or just delete it). ☠️
packages/zod/src/schemas.ts (3)
290-301
: 🛠️ Refactor suggestionDuplicate
guid()
assignment – drop the second alias
inst.guid
is assigned twice (line 294
and again online 300
).
The second assignment is redundant and risks masking the first one if the implementation is ever changed independently.inst.guid = (params) => inst.check(core._guid(ZodGUID, params)); @@ - inst.guid = (params) => inst.check(core._guid(ZodGUID, params));
813-818
: 🛠️ Refactor suggestionRepeated method wiring in
ZodBigInt
gt/gte/min
are wired twice. Besides the noise, this can confuse stack-traces and IDE “go-to-definition”.- inst.gte = (value, params) => inst.check(checks.gte(value, params)); - inst.min = (value, params) => inst.check(checks.gte(value, params)); - inst.gt = (value, params) => inst.check(checks.gt(value, params)); - inst.gte = (value, params) => inst.check(checks.gte(value, params)); - inst.min = (value, params) => inst.check(checks.gte(value, params)); + // gt / gte / min already assigned above – second pass removed
1025-1034
: 🧹 Nitpick (assertive)Minor: could avoid eager
cleanInterfaceShape
inkeyof()
Every
keyof()
call recalculatescleanInterfaceShape
.
Consider caching the result (e.g. onschema._zod
) to avoid unnecessary work inside hot-paths.This isn’t blocking, just something to keep in mind if profiling shows
keyof()
getting hit a lot.
♻️ Duplicate comments (1)
packages/mini/tests/interface.test.ts (1)
261-266
: Assertion relies onshape.b
being optional – CI currently redThe failing pipeline shows that
shape.b
is coming back as a plain
ZodString
, not aZodOptional
. This ties back to the bug in
partialObjectLike()
and possiblyextend()
. Once that fix lands, this
assertion should pass; until then, expect this test to fail.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
packages/core/src/api.ts
(8 hunks)packages/core/src/json-schema.ts
(0 hunks)packages/core/src/schemas.ts
(25 hunks)packages/core/src/to-json-schema.ts
(3 hunks)packages/core/src/util.ts
(6 hunks)packages/mini/src/schemas.ts
(11 hunks)packages/mini/tests/interface.test.ts
(2 hunks)packages/mini/tests/object.test.ts
(2 hunks)packages/tsc/bisect.ts
(1 hunks)packages/tsc/generate.ts
(1 hunks)packages/tsc/package.json
(1 hunks)packages/zod/src/schemas.ts
(11 hunks)packages/zod/tests/interface.test.ts
(3 hunks)packages/zod/tests/json-schema.test.ts
(2 hunks)packages/zod/tests/lazy.test.ts
(2 hunks)packages/zod/tests/partial-interface.test.ts
(6 hunks)packages/zod/tests/pickomit-interface.test.ts
(2 hunks)packages/zod/tests/pickomit-object.test.ts
(2 hunks)play.ts
(1 hunks)
💤 Files with no reviewable changes (1)
- packages/core/src/json-schema.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/zod/tests/partial-interface.test.ts (3)
packages/mini/tests/interface.test.ts (1)
z
(174-176)packages/mini/src/schemas.ts (1)
object
(820-836)packages/zod/src/schemas.ts (1)
object
(1408-1424)
packages/core/src/api.ts (3)
packages/core/src/schemas.ts (3)
$ZodInterface
(1917-1922)$ZodInterface
(1924-1929)$ZodInterfaceDef
(1893-1897)packages/core/src/util.ts (3)
shape
(741-745)shape
(758-763)shape
(776-781)packages/mini/src/schemas.ts (6)
shape
(741-746)shape
(765-770)shape
(789-794)shape
(827-829)shape
(847-849)shape
(867-869)
🪛 GitHub Check: Test with TypeScript 5.5 on Node latest
packages/zod/tests/interface.test.ts
[failure] 220-220: tests/interface.test.ts > .partial
AssertionError: expected ZodString{ _zod: { …(11) }, …(75) } to be an instance of ZodOptional
❯ tests/interface.test.ts:220:42
🪛 GitHub Check: Test with TypeScript latest on Node latest
packages/zod/tests/interface.test.ts
[failure] 220-220: tests/interface.test.ts > .partial
AssertionError: expected ZodString{ _zod: { …(11) }, …(75) } to be an instance of ZodOptional
❯ tests/interface.test.ts:220:42
🪛 GitHub Actions: test
packages/zod/tests/interface.test.ts
[error] 173-173: AssertionError: expected true to deeply equal false in test 'extend overrides with .shape'
[error] 220-220: AssertionError: expected ZodString instance to be an instance of ZodOptional in test '.partial'
packages/mini/tests/interface.test.ts
[error] 173-173: AssertionError: expected true to deeply equal false in test 'extend overrides with .shape'
[error] 220-220: AssertionError: expected ZodString instance to be an instance of ZodOptional in test '.partial'
packages/zod/tests/json-schema.test.ts
[error] 634-634: ReferenceError: Cannot access 'categorySchema' before initialization
🔇 Additional comments (31)
packages/tsc/bisect.ts (1)
51-51
: Switched from ZOD to ARKTYPE schemas in bisect testingThe change from ZOD to ARKTYPE in the bisect script aligns with similar changes elsewhere in the codebase. It's part of a coordinated update to use ArkType instead of Zod for performance testing.
packages/tsc/generate.ts (1)
28-30
: Updated schema generation to use ArkType with fewer test schemasThe shift from ZOD to ARKTYPE configuration and reducing the test schema count from 40 to 25 makes sense as part of the larger testing strategy changes. This should make the tests run faster while still providing meaningful performance data.
packages/tsc/package.json (2)
11-11
: Updated ArkType dependency to stable versionGood move switching from what was likely a release candidate to the stable version with caret range. This allows for compatible updates without being locked to a specific RC.
17-17
:✅ Verification successful
Updated Zod4 dependency to newer beta
The update to a newer beta version of Zod4 makes sense alongside the other dependency changes. Just be aware this is still using a beta version with a timestamp.
🏁 Script executed:
#!/bin/bash # Check if this beta version is still the latest available npm view zod dist-tags --json | grep betaLength of output: 109
zod4 bump confirmed as latest beta
Nice work! I checked
npm view zod dist-tags --json
and thenext
tag points to 4.0.0-beta.20250420T053007, so you’re on the freshest Zod4 beta.play.ts (2)
2-2
: Added English locale configurationNice addition of explicit locale configuration - makes the examples clearer by ensuring consistent error messages.
4-13
: Added helpful example comment for interface schemasThe multi-line comment with an example of using
z.interface
provides valuable context for the optionality changes in this PR. The example clearly demonstrates the new syntax for optional properties using the"b?"
notation.packages/zod/tests/lazy.test.ts (2)
5-5
: Updated test name to match the new optionality modelThe test name has been updated from "qin qout passthrough" to "opt passthrough", aligning with the refactoring that consolidates the separate input/output optionality flags into a single unified opt property.
39-41
: Updated assertions to verify the new optionality modelNice job updating the assertions to check the new
_zod.opt
property values instead of the previousqin
/qout
flags. This correctly validates that lazy schemas preserve optionality information with the new unified representation.packages/mini/tests/object.test.ts (3)
13-13
: Updated access path to reflect new shape metadata structureThe assertion has been correctly updated to use the new nested
.type._zod.opt
property path instead of the previous direct.points._zod.qout
access. This aligns with the core refactoring of optionality representation.
127-127
: Fixed missing closing braceAdded the missing closing brace and parenthesis for the "z.partial" test. Good catch!
129-139
: Added test for partial with mask functionalityCool new test case that verifies the behavior of
z.partial
when used with a mask object. This ensures that selective optional property transformation works correctly with the new shape metadata structure.packages/zod/tests/pickomit-object.test.ts (2)
46-52
: Updated optionality assertions to use the new shape metadata structureThe test now correctly creates interface schemas with explicit optionality markers (using "b?") and verifies optionality via the
.optionality
property instead of checking a separate optional keys array. This properly tests the new shape metadata approach.
87-90
: Updated omit test to use the new shape metadata structureSimilar to the pick test, this correctly updates the omit test to align with the new optionality representation in shape metadata.
packages/zod/tests/partial-interface.test.ts (5)
45-55
: Updated required object assertions to use the new shape metadata structureThe test now accesses schema types through the new
.type
property and uses.unwrap()
to check underlying types. This properly verifies that the required object transformation works with the new shape metadata structure.
89-92
: Updated required with mask assertions to use shape metadataTests for masked required transformations have been correctly updated to check types through the
.type
property, consistent with the new shape metadata approach.
105-108
: Updated assertions for required with falsy mask valuesThese assertions properly verify that falsy mask values are ignored when applying required transformations, using the new shape metadata structure.
121-128
: Updated partial with mask assertions to check both type and optionalityGreat job updating these assertions to verify both the
.type
and.optionality
properties. This comprehensive check ensures that the partial masking functionality correctly transforms properties while preserving their underlying types.
145-148
: Added verification of optionality states before and after transformationNice addition to validate both the original and transformed optionality states when applying partial transformations with falsy mask values. This ensures the masking logic correctly preserves required properties.
packages/zod/tests/interface.test.ts (3)
148-154
: 👍 New “extend with .shape” pattern looks goodNice switch to the spread-merge idiom – reads clearly and avoids the removed
extend()
semantics.
224-232
: Sanity-check.required()
internalsThese checks still rely on internal classes. If the refactor ever swaps
ZodNonOptional
out, tests will break again.
Consider asserting against.optionality === "required"
instead for future-proofing.
434-447
: Duplication test LGTMGood guard to ensure types remain identical after shape spreading.
packages/zod/tests/pickomit-interface.test.ts (1)
102-109
: Catch-all optionality inference test is solidThe type-level expectations using
expectTypeOf
are clear and stable – nice!packages/zod/tests/json-schema.test.ts (4)
1177-1201
: Snapshot change:$defs
+$ref
looks correctMoving the shared string schema to
$defs/__schema0
and re-using it via$ref
is the right call whenreused:"ref"
is requested. Snapshot update looks good.
1332-1340
: New_ref
coverage – 👍Great to see explicit tests ensuring description metadata bubbles out of wrappers.
1342-1357
: Lazy / optional / promise metadata propagationSame as above – these tests guard a subtle area and will save us from regressions.
1359-1429
: Input vs Output JSON-Schema mismatch spottedThe new test highlights differing
required
arrays between input & output modes – love it. One concern though: theoutput
mode marks keyb
(plain optional) as not required (✅) but also upgradesc
(defaulted) to required (✅). Behaviour seems consistent with the new logic into-json-schema.ts
; just keep an eye on user expectations.packages/core/src/to-json-schema.ts (2)
285-288
: Good call: process.type
instead of whole meta objectUsing
shape[key].type
avoids accidentally passing the metadata wrapper into recursion.
685-686
: Double-flatten call looks fineHooking
flattenRef
for both the live schema and any extracted$def
keeps them in sync. No issues.packages/mini/tests/interface.test.ts (1)
221-257
: Large blocks of commented-out testsWhole suites for
z.extend
have been disabled. That hides potential
regressions (and we’ve already seen one in CI). Either fix the tests to
match the new semantics or delete them entirely—commented code tends to
rot.packages/core/src/schemas.ts (1)
3055-3072
: Nice touch on the unifiedopt
flagThe new single-source-of-truth (
"optional" | "defaulted" | "required"
) cleans up the
oldqin/qout
duality and makes type-level reasoning way easier. 👍packages/mini/src/schemas.ts (1)
896-905
:extend()
/merge()
now bypass type-guard branchesWith the fallback to
util.extend(schema, shape)
the specialised handling for
ZodMiniInterface
vsZodMiniObject
is lost, meaning:
- Passing an instance instead of a raw shape silently falls through.
- Shape key collisions aren’t checked.
If that’s intentional, awesome – but it’s worth double-checking because it
changes previous behaviour.
play.ts
Outdated
const a = z.json(); | ||
type a = z.output<typeof a>; | ||
// z.parse(a, "hello"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Added JSON schema example
Adding a simple example of using z.json()
with a corresponding type alias is helpful. Consider expanding this with a parsing example to demonstrate usage.
const a = z.json();
type a = z.output<typeof a>;
-// z.parse(a, "hello");
+
+// Example usage:
+try {
+ const result = z.parse(a, '{"key": "value"}');
+ console.log("Parsed JSON:", result);
+} catch (error) {
+ console.error("Invalid JSON:", error);
+}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const a = z.json(); | |
type a = z.output<typeof a>; | |
// z.parse(a, "hello"); | |
const a = z.json(); | |
type a = z.output<typeof a>; | |
// Example usage: | |
try { | |
const result = z.parse(a, '{"key": "value"}'); | |
console.log("Parsed JSON:", result); | |
} catch (error) { | |
console.error("Invalid JSON:", error); | |
} |
packages/zod/tests/interface.test.ts
Outdated
// test("extend overrides existing", async () => { | ||
// const a = z.interface({ a: z.string() }); | ||
// const b = z.interface({ a: z.number() }); | ||
// const c = a.extend(b); | ||
// type c = z.infer<typeof c>; | ||
// expectTypeOf<c>().toEqualTypeOf<{ a: number }>(); | ||
// }); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Dead-code hanging around 🎣
These legacy .extend()
tests are now fully commented out. Keeping large commented blocks makes the test file noisy and harder to skim. If we don’t intend to revive them, let’s delete them (git will remember).
packages/zod/tests/interface.test.ts
Outdated
expect(Partial._zod.def.shape.a!.type).toBeInstanceOf(z.ZodOptional); | ||
expect(Partial._zod.def.shape.b!.type).toBeInstanceOf(z.ZodOptional); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broken .partial()
expectation
partial()
keeps the original bare type but flips optionality
, it does not wrap the type in ZodOptional
any more.
That’s why CI shouts “expected ZodString to be instance of ZodOptional”.
Replace the instance-checks with an optionality
check:
-expect(Partial._zod.def.shape.a!.type).toBeInstanceOf(z.ZodOptional);
-expect(Partial._zod.def.shape.b!.type).toBeInstanceOf(z.ZodOptional);
+expect(Partial._zod.def.shape.a!.optionality).toBe("optional");
+expect(Partial._zod.def.shape.b!.optionality).toBe("optional");
This should green-light the failing test.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
expect(Partial._zod.def.shape.a!.type).toBeInstanceOf(z.ZodOptional); | |
expect(Partial._zod.def.shape.b!.type).toBeInstanceOf(z.ZodOptional); | |
}); | |
expect(Partial._zod.def.shape.a!.optionality).toBe("optional"); | |
expect(Partial._zod.def.shape.b!.optionality).toBe("optional"); | |
}); |
🧰 Tools
🪛 GitHub Check: Test with TypeScript 5.5 on Node latest
[failure] 220-220: tests/interface.test.ts > .partial
AssertionError: expected ZodString{ _zod: { …(11) }, …(75) } to be an instance of ZodOptional
❯ tests/interface.test.ts:220:42
🪛 GitHub Check: Test with TypeScript latest on Node latest
[failure] 220-220: tests/interface.test.ts > .partial
AssertionError: expected ZodString{ _zod: { …(11) }, …(75) } to be an instance of ZodOptional
❯ tests/interface.test.ts:220:42
🪛 GitHub Actions: test
[error] 220-220: AssertionError: expected ZodString instance to be an instance of ZodOptional in test '.partial'
test("omit - remove optional", () => { | ||
const schema = z.interface({ a: z.string(), "b?": z.string() }); | ||
expect(schema.omit({ a: true })._zod.def.optional).toEqual(["b"]); | ||
expect(schema.omit({ b: true })._zod.def.optional).toEqual([]); | ||
expect(schema._zod.def.shape.a.optionality).toEqual("required"); | ||
expect("a" in schema.omit({ a: true })._zod.def.shape).toEqual(false); | ||
// expect(schema.omit({ b: true })._zod.def.optional).toEqual([]); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Same comment as above – avoid _zod
spelunking
These omit-tests reach into internals. Prefer behavioural checks.
const schema = z.interface({ a: z.string(), "b?": z.string() }); | ||
expect(schema._zod.def.shape.a.optionality).toEqual("required"); | ||
expect("b?" in schema._zod.def.shape).toEqual(false); | ||
expect("b" in schema._zod.def.shape).toEqual(true); | ||
expect(schema._zod.def.shape.b!.optionality).toEqual("optional"); | ||
expect(schema.pick({ a: true })._zod.def.shape.a.optionality).toEqual("required"); | ||
expect("b" in schema.pick({ a: true })._zod.def.shape).toEqual(false); | ||
expect("a" in schema.pick({ b: true })._zod.def.shape).toEqual(false); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
White-box assertions may be brittle
We’re poking _zod.def.shape.*
which is private API. Given the recent refactor, touching these internals caused most of the churn. If the goal is just to ensure optionality flags survive pick()
, a black-box parse test would be safer.
Optional suggestion: assert that schema.pick({ a:true }).parse({}).a
is undefined
, rather than checking the internal flag.
packages/core/src/api.ts
Outdated
// shape: shape ?? {}, | ||
get shape() { | ||
return util.objectShapeMeta(shape ?? {}); | ||
}, | ||
...util.normalizeParams(params), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Getter allocates fresh object every access
_object
’s shape
getter calls objectShapeMeta(shape ?? {})
on every
access, even though the result is immutable. Consider caching with
assignProp()
(same pattern you use elsewhere) to avoid repeated work.
get shape() {
- return util.objectShapeMeta(shape ?? {});
+ const _shape = util.objectShapeMeta(shape ?? {});
+ util.assignProp(this, "shape", _shape);
+ return _shape;
}
Tiny optimization but basically free.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// shape: shape ?? {}, | |
get shape() { | |
return util.objectShapeMeta(shape ?? {}); | |
}, | |
...util.normalizeParams(params), | |
// shape: shape ?? {}, | |
get shape() { | |
const _shape = util.objectShapeMeta(shape ?? {}); | |
util.assignProp(this, "shape", _shape); | |
return _shape; | |
}, | |
...util.normalizeParams(params), |
packages/core/src/schemas.ts
Outdated
$ZodType.init(inst, def); | ||
const items = def.items; | ||
const optStart = items.length - [...items].reverse().findIndex((item) => item._zod.qout !== "true"); | ||
const optStart = items.length - [...items].reverse().findIndex((item) => item._zod.opt !== "optional"); | ||
|
||
inst._zod.parse = (payload, ctx) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off-by-one in tuple length validation
optStart
is computed like this:
const optStart = items.length - [...items].reverse().findIndex(
(item) => item._zod.opt !== "optional",
);
When the tuple contains no optional elements, findIndex
returns -1
, so
optStart === items.length + 1
. Later:
const tooSmall = input.length < optStart - 1;
For an all-required 3-item tuple tooSmall
becomes input.length < 3
⇢ a 2-element
input silently passes validation. Yikes.
A safer compute:
-const optStart = items.length - [...items].reverse().findIndex((item) => item._zod.opt !== "optional");
+const optStart = (() => {
+ const firstOptional = items.findIndex((i) => i._zod.opt !== "required");
+ return firstOptional === -1 ? items.length : firstOptional;
+})();
Now optStart
equals the number of required elements, and the tooSmall
check works for every mix of required/optional/defaulted items.
packages/core/src/schemas.ts
Outdated
keys, | ||
keySet: new Set(keys), | ||
numKeys: keys.length, | ||
optionalKeys: new Set(def.optional), | ||
optionalKeys: new Set(keys.filter((key) => def.shape[key].optionality === "optional")), | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
optionalKeys
should also include “defaulted” fields
A "defaulted"
property is still optional on input (the default will kick-in), so it belongs in optionalKeys
.
Current filter ignores them, which means the fast-path could treat defaulted keys as required and raise false errors.
- optionalKeys: new Set(keys.filter((key) => def.shape[key].optionality === "optional")),
+ optionalKeys: new Set(
+ keys.filter((key) => {
+ const opt = def.shape[key].optionality;
+ return opt === "optional" || opt === "defaulted";
+ }),
+ ),
Adds one conditional and keeps behaviour consistent with the runtime parser below.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
keys, | |
keySet: new Set(keys), | |
numKeys: keys.length, | |
optionalKeys: new Set(def.optional), | |
optionalKeys: new Set(keys.filter((key) => def.shape[key].optionality === "optional")), | |
}; | |
keys, | |
keySet: new Set(keys), | |
numKeys: keys.length, | |
optionalKeys: new Set( | |
keys.filter((key) => { | |
const opt = def.shape[key].optionality; | |
return opt === "optional" || opt === "defaulted"; | |
}), | |
), | |
}; |
packages/mini/src/schemas.ts
Outdated
console.log("sup"); | ||
if (schema instanceof ZodMiniInterface) return util.partialObjectLike(null, schema, mask); | ||
if (schema instanceof ZodMiniObject) return util.partialObjectLike(ZodMiniOptional, schema, mask); | ||
return schema; // base case: shape |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Debug console.log
slipped through
A stray console.log("sup")
made it into partial()
.
Libraries shouldn’t leak debugging noise to consumer consoles.
- console.log("sup");
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
console.log("sup"); | |
if (schema instanceof ZodMiniInterface) return util.partialObjectLike(null, schema, mask); | |
if (schema instanceof ZodMiniObject) return util.partialObjectLike(ZodMiniOptional, schema, mask); | |
return schema; // base case: shape | |
if (schema instanceof ZodMiniInterface) return util.partialObjectLike(null, schema, mask); | |
if (schema instanceof ZodMiniObject) return util.partialObjectLike(ZodMiniOptional, schema, mask); | |
return schema; // base case: shape |
z.object()
z.object()
, drop z..interface()
…. remove ?-prefixing for defaulted. handle optionals/defaults in json schema
96fde46
to
7c404f7
Compare
z.object()
, drop z..interface()
z.object()
, drop z.interface()
|
* feat(lang): Added initial Azerbaijani locale (#4158) * feat(lang): Added initial Azerbaijani locale * Format --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * fix(docs): Typo fro (#4157) * Fix broken logo link (#3974) * Fix broken logo link * Use absolute URL * Absolute url --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Add packages/docs * Remove packages/docs * Clean up zodmini exports with underscores * Add precommit format * Fix error generic * Fix optionality docs * tweak (#4173) Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * fix: dev:play script to use play.ts instead of playground.ts (#4177) * Optimize `extendShape` (#4150) Co-authored-by: Anders Hejlsberg <andersh@microsoft.com> * 3.24.3 * add back packages/docs * Update * Update benchmark * Refactor `Extend` to preserve declaration links (#4155) * Refactor `ExtendShape` to preserve declaration links * fix type issues * Simplify aliases * Fix lint * Fix * Update benchmarks --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Fix en.ts * 0.5.2 * 0.6.0 * Fix enum type portability * Clean up passthrough schemas in toJSONSchema * 0.6.3 * v4: fix typos in basics guide (#4211) - Removed redundant whitespaces before `string()` method - Fixed typo in the note to `.parse()` method * v4: fix typo in defining schemas section (#4219) * fix migration guide typos (#4205) * partial record * Clean up toJSONSchema behavior. extract schemas with ids * Improve core.mdx * Fix missing methods * 0.7.0 * Fix unrepresentable literals and onattach issues * 0.8.0 * Improve error customization docs * Clarify error docs * Closes #4089 * Fix starts_with * docs: add zod-schema-faker to ecosystem (#4247) * fix(docs): Correct "with with" typo. (#4243) Correct "with with" typo. * fix(docs): Correct typo in Records (#4244) Correct typo in Records "that as" instead of "that is". * fix(docs): Typo in `z.object()` vs `z.interface()` (#4241) Update documentation to clarify that optional properties in `z.interface()` are defined with a `?` suffix, not a prefix. * Correcting the default numeric range to be inclusive. (#4224) * Reusing `Number.MAX_VALUE` for `float64` range (#4222) * Clarify defaults for `toJsonSchema` options (#4223) * docs: add GQLoom to ecosystem (#4225) * fix(docs): Swap Enum `.exclude() / .extract()` code samples. (#4242) The code samples in `.exclude()` belong to `.extract()` and vice versa. * fix(docs): Grammatical error in Refinements (#4252) Correct grammatical error in Refinements * Improve handling of `.meta()`/`.describe()` in JSON Schema converter (#4255) * WIP * Big improvements to meta and describe handling * WIP * Lazy disc * Add cybozu * 0.9.0 * Feature Stainless * Edit alt text * doc: fixed missing comment in migration guide (#4293) * fix migration guide typo (#4291) * Add "zod-config" to ecosystem.tsx (v4 support via pre-release) (#4282) * Update ecosystem.tsx to add "zod-config" with v4 support via RC * fix(docs): Correct formatting in ecosystem.tsx for "zod-config" entry --------- Co-authored-by: Alexandre Marques <a.marques@epilot.cloud> * docs: add support for `kubb` as part of the v4 upgrade (#4260) * Add methods to string formats (#4296) * 0.10.0 * Tweak * Tweak * z._default * Correct conditions for error in docs (#4312) - Make schema a strict object - Add extra key to induce error described later * fix vscode debug issue with tsx runtime (#4306) * Tweak default docs * correction in documentation (#4303) (zod example using @zod/mini import) * WIP * Make ulid case insensitive * v0.10.1 * Improve jsdoc * ULID * docs: in v4, `strict()` is not deprecated (#4298) * docs: in v4, `strict()` is not deprecated * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * chore: add `orval` to XtoZod ecosystems in v4 (#4256) Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Add composable-functions as a library powered by Zod supporting v4 (#4261) * Add composable-functions as a library powered by Zod supporting v4 * docs: in v4, `strict()` is not deprecated (#4298) * docs: in v4, `strict()` is not deprecated * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * chore: add `orval` to XtoZod ecosystems in v4 (#4256) Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> --------- Co-authored-by: Shodai Suzuki <info@soartec-lab.work> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Support recursive types in `z.object()`, drop `z..interface()` (#4271) * WIP * WIP * Add qin * WIP * Refactor object-like internals. use optionality in zodinterface shape. remove ?-prefixing for defaulted. handle optionals/defaults in json schema * WIP * WIP * Generics are working * WIP * WIP * Fix * Fix tests * Fix tests * WIP * WIP * Drop `z.interface()` from docs (#4316) * Drop interface * Format * Feature * Allow HH:MM format in `z.string().datetime()` and `z.string().time()` (#4315) * feat: allow omitting seconds in time string * docs: include examples of omitted seconds * docs: remove whitespace * 3.23.4 * V4(fix): generate json-schema with min/max(0) (#4267) * fix: generate number json schema * fix: generate json schema with min/max(0) * Apply suggestions from code review fix: format Co-authored-by: Anna Bocharova <robin_tail@me.com> --------- Co-authored-by: Panda <panda@mail.ru> Co-authored-by: Anna Bocharova <robin_tail@me.com> * 0.11.1 * docs: added @regle/schemas to form ecosystem (#4169) * docs: added @regle/schemas to form ecosystem In my previous MR you said to edit `components/ecosystem.mdx` but couldn't find it, if it was a typo i guessed it was this file! 👍 * Add to ecosystem.tsx --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Fix union inferred type issue * v0.11.2 * optionalObjectKeys -> optionalKeys * 0.11.3 * Add test for union types * Fix assignability * 0.11.4 * Add assignability tests * Stack trace in parse and parseAsync * Biome * Clean up code, fix lock * Document default changes * Clarify deprecations * Update changelog * v0.11.6. support recursive types in zodmini * Add recursive type tests for zod mini * Add recursive tests for zod mini * fix(core): use `Object.create(null)` to cache ids (#4359) * Jazz sponsorship * Update Speakeasy sponsor URL (#4337) * fix: use input, not output, for input of ZodUnionInternals (#4330) * Languages: Add Japanese language (#4170) * Add Japanese locale * format * fix(ja): Fixed comparison operator in error messages * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * New parse signature * Fix parse functions * Add Jazz * Languages: Add Portuguese language (#4171) * Adding portuguese translations * Adding portuguese * Adding language to docs * Adding spanish language in docs * Tweak * Updating references from string to texto * Updating faixa to faixa de * Updating from elementos to itens * Mirroring validation from original file * Updating regex to padrao * Updating entrada by tipo * Replacing entrada by campo --------- Co-authored-by: Helmer <11061182+helmerdavila@users.noreply.github.com> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: Add french language (#4172) * Adding french and exporting it * Adding french in docs * Update fr.ts Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev> * Translating datetime * Tweak * Update elements * Update packages/core/src/locales/fr.ts Co-authored-by: Fabrice Cipolla <fabricecipolla@gmail.com> --------- Co-authored-by: Helmer <11061182+helmerdavila@users.noreply.github.com> Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: Fabrice Cipolla <fabricecipolla@gmail.com> * Languages: Add Arabic language (#4176) * feat(lang): Add Arabic locale support * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * remove period * fix: add space after comparison operator * feat(lang): Add Arabic locale support * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * Update packages/core/src/locales/ar.ts Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> * remove period * fix: add space after comparison operator * Tweak * Updated ar.ts with new edits discussed in #4176 * Update packages/core/src/locales/ar.ts * Update packages/core/src/locales/ar.ts * Update packages/core/src/locales/ar.ts * chore: format * fix: update translation for template_literal * fix: correct translation for invalid input error message * fix: spacing in error messages * fix: show correct prefix * fix: remove unused template literal * Update error-customization.mdx --------- Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: Hosam Hamdy <hosam5553@gmail.com> * Languages: Add Hebrew language (#4183) * Create he.ts * Update locales.ts * Update error-customization.mdx * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: add Polish 🇵🇱 language (#4184) * Languages: add polish language * Fix: apply formatting * Tweak * Fix: add missing fallbacks * Update packages/core/src/locales/pl.ts Co-authored-by: Marcel <51132547+marceleq27@users.noreply.github.com> * Update packages/core/src/locales/pl.ts Co-authored-by: Marcel <51132547+marceleq27@users.noreply.github.com> --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: Marcel <51132547+marceleq27@users.noreply.github.com> * Languages: add Ukrainian language (#4185) * locale: add ukrainian language * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: add Finnish language (#4188) * feat: add initial finnish translations * chore: add export and docs * Tweak * feat: add more translations for different data types * fix: lint errors * fix: map translation * feat: only handle used cases in TypeNames * refactor: pr comments * refactor: map translation * refactor: heavily simplify finnish translations * chore: use unioni instead of yhdiste Might be more commonly used (gut feeling). * style: remove unused imports --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: add Hungarian language (#4186) * add hungarian * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Add Simplified Chinese (#4189) * feat(lang): add Simplified Chinese * fix: locale * fix: space format * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(lang): add Vietnamese (vi) locale (#4191) * feat(lang): add Vietnamese (vi) locale * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: add Czech language 🇨🇿 (#4192) * feat: add czech error locales * fix(locales): improve Czech error messages for clarity * Fmt * fix(locales): use correct string error prefix in error message For Czech language. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(locales): translate other data types as well For Czech language. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(locales): handle nullable variables in error messages For Czech langauge. --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Languages: add italian language (#4194) * feat: added italian language * Fmt * Tweak --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: add Indonesia language (#4195) * Languages: add Bahasa Indonesia language * tweak * Tweak * Update packages/core/src/locales/id.ts Co-authored-by: Griko Nibras <griko@nibras.co> * Update packages/core/src/locales/id.ts Co-authored-by: Griko Nibras <griko@nibras.co> * Update packages/core/src/locales/id.ts Co-authored-by: Griko Nibras <griko@nibras.co> * Update packages/core/src/locales/id.ts Co-authored-by: Griko Nibras <griko@nibras.co> * Update packages/core/src/locales/id.ts Co-authored-by: Griko Nibras <griko@nibras.co> --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: Griko Nibras <griko@nibras.co> * feat(lang): Added initial Turkish locale (#4196) * feat(lang): Added initial Turkish locale * fix(lang): Improve formatting in Turkish error messages * fix(lang): Update Turkish translations for various terms * fix(lang): Update Turkish translations for base64 and template literal terms * fix(lang): Correct Turkish error message for 'starts_with' format * test(lang): Add unit tests for Turkish locale parsing and validation * test(lang): Remove console log from Turkish locale test for cleaner output Co-authored-by: Mert Şişmanoğlu <mertssmnoglu@gmail.com> * fix(lang): Correct variable reference in Turkish 'starts_with' error message Co-authored-by: Mert Şişmanoğlu <mertssmnoglu@gmail.com> --------- Co-authored-by: Mert Şişmanoğlu <mertssmnoglu@gmail.com> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(locale): add russian language (#4197) * feat(locale): add russian language * updates * feat(locale): add Russian pluralization rules * fix: tests * fixes * fix(locale): correct error message for string format validation in Russian locale --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(locale): add belarusian language (#4199) * feat(locale): add belarusian language * feat: add belarusian plurals support * fix(locale): correct Belarusian error message formatting for string validation --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(lang): Add ota locale in Latin script (#4200) Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: Add Korean language (#4206) * chore: run format * feat(locale): add Korean language --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: Add Farsi language (#4207) * feat(lang): add farsi language * fix: joinValues in invalid_value --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(locale): Add Urdu language (#4208) * feat(locale): add urdu language * Fix formatting for ur.ts file * Fix formatting for ur.ts file --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Add Macedonian locale support and update documentation (#4214) Co-authored-by: Antonio Ivanovski <antonio-ivanovski@users.noreply.github.com> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Add-fr-ca-locale (#4218) * chore: Add .pnpm-store to .gitignore * chore: Add Canadian French locale support --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(locale): add Traditional Chinese (#4226) * feat(locale): add Traditional Chinese * fix --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(locale): Add Norwegian (#4229) * feat: added Norwegian (Bokmål) * fix: renamed locale to no + improved translations * fix: forgot to add the file to git... * fix: more accurate verb for collections * chore: removed commented out code * chore: ran linter * fix: minor tweaks to error messages * Update packages/core/src/locales/no.ts I agree, that's better! Co-authored-by: Steffen Holanger <44159536+spiftire@users.noreply.github.com> --------- Co-authored-by: Magne Skutle <magne.skutle@lyse.no> Co-authored-by: Steffen Holanger <44159536+spiftire@users.noreply.github.com> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(lang): Added initial Thai locale (#4230) * feat(lang): Added initial Thai locale * fix(lang): improve thai translation for clarify meaning --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(locale): Add SL locale (#4231) Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * feat(lang): Added initial `Tamil` locale (#4237) Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: add German language (#4253) * feat(locales): add Catalan locale support and update documentation (#4287) Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Languages: Add Malay language (#4204) * feat(lang): add Malay support * chore: run format --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Fix issue issue * Subclass Error * Update Error inheritance * Clean up Error inheritance * Fix prom test * Refactor: add subpath imports `zod/v4` (#4364) * Refactor to subpaths * Tweak * Roll back to tshy 1 * Use supershy * Lint * drop dryrun * Add root re-export * Add file * Update docs * Update guide * Docs and locales barrel file * Update announcement and changelog * Update anno * Add accordion * Fix readonly issue on zodminienum. Rename _getter to innerType on zodlazy * Fix diagonal enum issue * Improve `z.url()`: support `hostname` and `protocol` regexes (#4367) * Improve z.url(). Add support for hostname and protocol regexes * Add docs * Add z.core.$ZodBranded * Add jitless config flag (#4368) * Remove util.Exactly from zod-mini masks * Roll back never toJSONSchema * Update snaps * v4: set additionalProperties to false when catchall is never (#4365) * Document dropping of ctx.path in changelog * Remove z.core from zod/v4-mini * Add back z.core * Document .refine overload drop * Fix input type for ZodMiniDate * Add zodpipe mini test * Clean up params * Fix test * docs: escape `.` in the example regex for `z.url` with `hostname` (#4375) * Document moduleResolution * typo * Implement `z.prefault` (#4376) * WIP * Implement prefault * Document prefault * Add link to function workaround * Defer initialization to avoid crashes with z.lazy. Closes #4324. (#4377) * noPrecompilation -> jitless * Update ZodError section in changelog. Deprecate isOptional and isNullable * Return data on best-effort basis despite errors * Fix prefixIssues issue * Add fallback File interface * Add back formatError and flattenError functions * Allow generic on coerce functions (#4379) * Allow generic on coerce functions * Clean up * Add union values test * Add another lazy schema init test * Clean up * Add docs for recursive type debugging. * Update recursive type docs * F * Improve discriminateduion errors (#4384) * Remove abort property from ZodTransformDef. Add discriminator key to no_discriminator_found issue. * Clean up * Clean up Error inheritance * Remove examples from JSON Schema when io=input * `z.toJSONSchema`: Defer execution of `override`, improve ref handling (#4385) * Defer override execution * Improve refs in json schema. Cleanup. * Clean up * Clean up * Clean up enum input type * Improve base64 perf. Disallow padding in base64url. (#4386) * Improve base64 perf. Disallow padding in base64url * Allow empty base64 string * Allow empty base64 string * Clean up * Document base64url change * Tweak * Fix treeify error invalid_union handling * fix ensure catchValue is applied correctly on validation error (#4382) * Simplify object generics, add test for empty objects (#4387) * Simplify object generics. Add tests for empty object types. * Fix tests * Fix tests, rename pickomit * Fix typo in json-schema.mdx (#4380) * fix: use JSON.stringify to escape toDotPath keys w/ quotes (#4374) This should also correctly handle even weirder stuff like newlines in proeprty keys. JSON.stringify should do all the right escaping - if it's a valid JSON string, it's a valid key. Note: one existing test was updated: ``` expect(z.core.toDotPath(["data[0]", "value"])).toMatchInlineSnapshot(`"data[0].value"`); // <- before expect(z.core.toDotPath(["data[0]", "value"])).toMatchInlineSnapshot(`"["data[0]"].value"`); // <- after ``` This looks correct to me? Previously it was implying a shape of `{data: [{value: '...'}]}` when actually it should be a shape of `{'data[0]': {value: '...'}}` (updated 12:30) Co-authored-by: Misha Kaletsky <mmkal@users.noreply.github.com> * Improve min/max in JSON Schema (#4388) * Add types for bag. Fix setting of min/max in json schema * Fix minValue/maxValue * Improve number tests * Trim * Remove type on base class * Fix err * Mark internals * Remove partial record accordion * Update ecosystem * Add reference to instanceof Class * Add JSON Schema (#4390) * Update prefault changelog * Lazy initialization in union and discunion * Clean up util * Fix override execution order * Add test for override execution order * Preserve shape key ordering in result (#4396) * Clean up object parser * Fix discriminated union * Add disc union test * Fix inheritance on ZodDiscriminatedUnion * Document metadata inheritance * Metadata inherited across clones (#4401) * Add back discriminator to `z.discriminatedUnion()` API (#4402) * WIP * Add back discriminator, add benchmark * Update docs * Play * Add disc union test, improve docs * chore: add oRPC to the API Libraries ecosystem (#4349) oRPC 1.2.0 has full support for Zod 4, from validation to OpenAPI spec generation Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> * Update benchmark * [v3] Allow missing `type` in JWT (#4404) * WIP * Improve JWT tests * Implement pipe optionality. Drop `._zod.optionality` in favor of `._zod.optin` and `._zod.optout`. (#4405) * Implement pipe optionalty. Switch to optin and optout * Clean up * Redirect * WIP * Page events * Move up sponsors * Try scroller * Suspense * No animation * Add redirects * Remove logs * Add Juno (new silver sponsor) (#4408) * Add Juno (new silver sponsor) * chore: redo * chore: redo * Add redirects * WIP * Audit redirects * Tweak * v4: Doc updates for release (#4409) * v4 release updates * Tweak * REmove banners * Add tabs * Update intro * Zod 4 * Fix lint * Simplify readme * Simplify readme * Simplify readme * Simplify readme --------- Co-authored-by: Chingiz Mammadov <info@chz.dev> Co-authored-by: Henrikh Kantuni <henrikh.kantuni@gmail.com> Co-authored-by: Shivam Vijaywargi <vijaywargishivam@gmail.com> Co-authored-by: Abdalrhman Almarakeby <contact@abdalrhman.me> Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com> Co-authored-by: Anders Hejlsberg <andersh@microsoft.com> Co-authored-by: Andrii Andreichenko <87246480+ShrewdLuni@users.noreply.github.com> Co-authored-by: Mohammed Alaa Alhaj <82370689+mhdalaa93@users.noreply.github.com> Co-authored-by: Jonathan Waltz <volcanicislander@gmail.com> Co-authored-by: Ernest <soc221b.e@gmail.com> Co-authored-by: Juan Giordana <juangiordana@gmail.com> Co-authored-by: Anna Bocharova <robin_tail@me.com> Co-authored-by: xcfox <809067559@qq.com> Co-authored-by: Shodai Suzuki <info@soartec-lab.work> Co-authored-by: 8D96 Dan Michael O. Heggø <danmichaelo@gmail.com> Co-authored-by: Alexandre Marques <pt.alexandremarques@gmail.com> Co-authored-by: Alexandre Marques <a.marques@epilot.cloud> Co-authored-by: Stijn Van Hulle <stijn.vanhulle@pm.me> Co-authored-by: George Kormaris <mail@gekorm.com> Co-authored-by: Lakshan Perera <39025880+0xluckycoder@users.noreply.github.com> Co-authored-by: Kevin Damm <github@kevindamm.com> Co-authored-by: Guga Guichard <gustavoguichard@gmail.com> Co-authored-by: Timothy Ng <5664347+timorthi@users.noreply.github.com> Co-authored-by: Panda <57873412+PandaWorker@users.noreply.github.com> Co-authored-by: Panda <panda@mail.ru> Co-authored-by: Victor Garcia <victorgarciaparis13@gmail.com> Co-authored-by: andrew jarrett <ahrjarrett@gmail.com> Co-authored-by: Nolan Di Mare Sullivan <nolan.dm.sullivan@gmail.com> Co-authored-by: Rik Brown <rik@rik.codes> Co-authored-by: Kio Yoshimatsu <69349613+yoshimatsu567@users.noreply.github.com> Co-authored-by: Helmer <11061182+helmerdx@users.noreply.github.com> Co-authored-by: Helmer <11061182+helmerdavila@users.noreply.github.com> Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev> Co-authored-by: Fabrice Cipolla <fabricecipolla@gmail.com> Co-authored-by: Abdullah Mohammed <554032+abodacs@users.noreply.github.com> Co-authored-by: Hosam Hamdy <hosam5553@gmail.com> Co-authored-by: Shachar Zidon <theonlytails@theonlytails.com> Co-authored-by: Bartosz Szar <szarbartosz@gmail.com> Co-authored-by: Marcel <51132547+marceleq27@users.noreply.github.com> Co-authored-by: Dmytro Dobrovolskyi <1dima9999@gmail.com> Co-authored-by: Henri Södergård <46006967+Heenkkk@users.noreply.github.com> Co-authored-by: Lőrik Levente <33373714+Levminer@users.noreply.github.com> Co-authored-by: GrahamQuan <33834833+GrahamQuan@users.noreply.github.com> Co-authored-by: Nguyễn Hoàng Trung <trungnh@solashi.com> Co-authored-by: Filip Ditrich <ditrich@nfctron.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Davide Lombardi <56124504+berz8@users.noreply.github.com> Co-authored-by: Krisna Wijaya <krisna.w2010@gmail.com> Co-authored-by: Griko Nibras <griko@nibras.co> Co-authored-by: Özgür ÖZALP <ozgurozalp1999@gmail.com> Co-authored-by: Mert Şişmanoğlu <mertssmnoglu@gmail.com> Co-authored-by: Vlad Sazonau <16666234+VladSez@users.noreply.github.com> Co-authored-by: KadimElifba <kadimelifba@gmail.com> Co-authored-by: Lee Keun Hwee <56650238+Geuni620@users.noreply.github.com> Co-authored-by: Mojtaba <45169963+Mojtaba-NA@users.noreply.github.com> Co-authored-by: Asharib Ali <asharibarain4@gmail.com> Co-authored-by: Antonio <1628876+antonio-ivanovski@users.noreply.github.com> Co-authored-by: Antonio Ivanovski <antonio-ivanovski@users.noreply.github.com> Co-authored-by: Sofiane <131229158+sohocine@users.noreply.github.com> Co-authored-by: Jie Peng <dean.leehom@gmail.com> Co-authored-by: Magne Skutle <magne.skutle@gmail.com> Co-authored-by: Magne Skutle <magne.skutle@lyse.no> Co-authored-by: Steffen Holanger <44159536+spiftire@users.noreply.github.com> Co-authored-by: Thada Wangthammang <thada.wth@gmail.com> Co-authored-by: Tadej Polajnar <40028548+TadejPolajnar@users.noreply.github.com> Co-authored-by: Sasivarnan R <sasivarnan@users.noreply.github.com> Co-authored-by: Simon Legner <Simon.Legner@gmail.com> Co-authored-by: Pere Montpeó <pmontp19@users.noreply.github.com> Co-authored-by: Khairul Haziq <ihaziqkhairi@gmail.com> Co-authored-by: Jacques <25390037+jecquas@users.noreply.github.com> Co-authored-by: Loris Sigrist <43482866+LorisSigrist@users.noreply.github.com> Co-authored-by: chimame <rito.tamata@gmail.com> Co-authored-by: Fabian Hiller <hillerfabian11@gmail.com> Co-authored-by: Misha Kaletsky <15040698+mmkal@users.noreply.github.com> Co-authored-by: Misha Kaletsky <mmkal@users.noreply.github.com> Co-authored-by: unnoq <contact@unnoq.com> Co-authored-by: David Dal Busco <david.dalbusco@outlook.com>
! I finally figured out a way to support cyclical type inference in
z.object()
. I've been trying to get this working for years and thought it was impossible.This new capability negates the primarily raison d'être of
z.interface()
. Thus this PR also:z.interface()
.core.ZodObjectLike
(an interface that served as a shared parent for ZodObject and ZodInterface)optional: string[]
fromZodObjectDef
. Optionality information is stored on the elements insideshape
.This is a major breaking change but Zod 4 is still in beta for two more weeks. I'm loathe to do a change of this magnitude but in general consolidating on a single object API is a huge win, so it's worth it.