8000 Support recursive types in `z.object()`, drop `z.interface()` by colinhacks · Pull Request #4271 · colinhacks/zod · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

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

Merged
merged 15 commits into from
May 4, 2025

Conversation

colinhacks
Copy link
Owner
@colinhacks colinhacks commented Apr 24, 2025

⚠️ This PR contains breaking changes. z.interface() has been removed in its entirety.

! 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.

Screenshot 2025-04-29 at 15 08 52

This new capability negates the primarily raison d'être of z.interface(). Thus this PR also:

  • Removes z.interface().
  • Removes core.ZodObjectLike (an interface that served as a shared parent for ZodObject and ZodInterface)
  • Removes optional: string[] from ZodObjectDef. Optionality information is stored on the elements inside shape.

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.

Copy link
Contributor
coderabbitai bot commented Apr 24, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This 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 opt property with explicit states: "optional", "defaulted", or "required". Shape metadata is now embedded directly within schema definitions, streamlining type inference and manipulation. Utility functions and schema factories are refactored to leverage this new metadata format, and related test suites are updated to match the new internal structure. JSON schema generation and schema manipulation methods are also revised to align with these changes.

Changes

File(s) / Area Change Summary
packages/core/src/schemas.ts, packages/core/src/util.ts, packages/core/src/api.ts Refactored schema internals to use a unified opt property for optionality ("optional", "defaulted", "required"), replacing separate input/output flags. Updated all related types, interfaces, and utility functions to operate on enriched shape metadata. Simplified type inference and manipulation utilities for objects and interfaces.
packages/core/src/to-json-schema.ts Adjusted JSON schema generation to use the new shape metadata and opt property for determining required fields. Enhanced reference flattening to support draft-7 compatibility.
packages/mini/src/schemas.ts Updated mini schema interfaces and methods to explicitly include shape metadata and handle optionality using the new system. Refactored utility functions (extend, merge, pick, omit, partial, required) to align with updated core logic.
packages/zod/src/schemas.ts Modernized ZodInterface and ZodObject types and methods to use explicit shape and extra property generics. Removed deprecated partial/required types, added new utility types, and updated all schema manipulation methods to rely on the new shape metadata and optionality tracking.
packages/core/src/json-schema.ts Removed a trailing blank line in the BaseSchema interface. No functional changes.
packages/mini/tests/interface.test.ts, packages/zod/tests/interface.test.ts Updated and refactored interface tests to match the new internal structure for optionality. Removed or commented out tests relying on deprecated or altered behaviors. Added new tests using .shape spreading for interface extension.
packages/mini/tests/object.test.ts, packages/zod/tests/partial-interface.test.ts, Updated tests to check the new .optionality property in shape metadata instead of previous flags or arrays. Added new tests for partial application with masks.
packages/zod/tests/pickomit-interface.test.ts, packages/zod/tests/pickomit-object.test.ts Refactored pick/omit tests to verify optionality using the .optionality property and updated type inference checks. Improved catchall inference tests.
packages/zod/tests/json-schema.test.ts Updated and added tests to verify JSON schema output for input/output modes and reference flattening. Adjusted expected outputs to match new schema generation logic.
packages/zod/tests/lazy.test.ts Changed test assertions to check the new opt property for lazy schemas, confirming correct propagation of optionality.
packages/tsc/generate.ts, packages/tsc/bisect.ts Switched schema generation to use ARKTYPE configuration instead of ZOD and reduced the number of generated schemas.
packages/tsc/package.json Updated arktype dependency to a caret range and bumped zod4 to a newer beta version.
play.ts Set English locale for zod messages, added a schema example, and cleaned up demo code.

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
Loading
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
Loading

Poem

🌱
Optional or required, now clearly defined,
With "opt" in the shape, no flags left behind.
Schemas are smarter, their types shining bright,
Parsing and picking with metadata in sight.
Tests all updated, the codebase feels new—
Here’s to clean shapes, and to strong typing too!


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@colinhacks
Copy link
Owner Author

@coderabbitai review

Copy link
Contributor
coderabbitai bot commented Apr 24, 2025
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor
@coderabbitai coderabbitai bot left a 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 issue

Failing assertion – safeParse now succeeds

CI reports that line 173 (expect(c.safeParse(aData).success).toEqual(false)) is red.
Because the merged c schema inherits optionality from a.shapeb.shape, aData actually satisfies c, so safeParse().success comes back true.

Either:

  1. The behaviour is correct → update the expectation to true.
  2. The behaviour should be “fail” → we need to strip "b" from a.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 – consider objectShapeMeta

extendObjectLike() merges two already-meta-fied shapes, but it doesn’t
pass the incoming b’s raw additions through objectShapeMeta.
If a caller mutates b’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 revive

The 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 issue

Debug console.log sneaked in

Looks 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 suggestion

Duplicate guid() assignment – drop the second alias

inst.guid is assigned twice (line 294 and again on line 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 suggestion

Repeated 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 in keyof()

Every keyof() call recalculates cleanInterfaceShape.
Consider caching the result (e.g. on schema._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 on shape.b being optional – CI currently red

The failing pipeline shows that shape.b is coming back as a plain
ZodString, not a ZodOptional. This ties back to the bug in
partialObjectLike() and possibly extend(). 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7051967 and 2b18cea.

⛔ 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 testing

The 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 schemas

The 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 version

Good 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 beta

Length of output: 109


zod4 bump confirmed as latest beta

Nice work! I checked npm view zod dist-tags --json and the next tag points to 4.0.0-beta.20250420T053007, so you’re on the freshest Zod4 beta.

play.ts (2)

2-2: Added English locale configuration

Nice addition of explicit locale configuration - makes the examples clearer by ensuring consistent error messages.


4-13: Added helpful example comment for interface schemas

The 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 model

The 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 model

Nice job updating the assertions to check the new _zod.opt property values instead of the previous qin/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 structure

The 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 brace

Added the missing closing brace and parenthesis for the "z.partial" test. Good catch!


129-139: Added test for partial with mask functionality

Cool 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 structure

The 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 structure

Similar 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 structure

The 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 metadata

Tests 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 values

These 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 optionality

Great 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 transformation

Nice 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 good

Nice switch to the spread-merge idiom – reads clearly and avoids the removed extend() semantics.


224-232: Sanity-check .required() internals

These 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 LGTM

Good 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 solid

The 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 correct

Moving the shared string schema to $defs/__schema0 and re-using it via $ref is the right call when reused:"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 propagation

Same as above – these tests guard a subtle area and will save us from regressions.


1359-1429: Input vs Output JSON-Schema mismatch spotted

The new test highlights differing required arrays between input & output modes – love it. One concern though: the output mode marks key b (plain optional) as not required (✅) but also upgrades c (defaulted) to required (✅). Behaviour seems consistent with the new logic in to-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 object

Using shape[key].type avoids accidentally passing the metadata wrapper into recursion.


685-686: Double-flatten call looks fine

Hooking 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 tests

Whole 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 unified opt flag

The new single-source-of-truth ("optional" | "defaulted" | "required") cleans up the
old qin/qout duality and makes type-level reasoning way easier. 👍

packages/mini/src/schemas.ts (1)

896-905: extend() / merge() now bypass type-guard branches

With the fallback to util.extend(schema, shape) the specialised handling for
ZodMiniInterface vs ZodMiniObject 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
Comment on lines 14 to 16
const a = z.json();
type a = z.output<typeof a>;
// z.parse(a, "hello");
Copy link
Contributor

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.

Suggested change
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);
}

Comment on lines 123 to 130
// 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 }>();
// });

Copy link
Contributor

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).

Comment on lines 220 to 222
expect(Partial._zod.def.shape.a!.type).toBeInstanceOf(z.ZodOptional);
expect(Partial._zod.def.shape.b!.type).toBeInstanceOf(z.ZodOptional);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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'

Comment on lines 92 to 97
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([]);
});
Copy link
Contributor

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.

Comment on lines 50 to 58
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);
});
Copy link
Contributor

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.

Comment on lines 1091 to 1095
// shape: shape ?? {},
get shape() {
return util.objectShapeMeta(shape ?? {});
},
...util.normalizeParams(params),
Copy link
Contributor

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.

Suggested change
// 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),

Comment on lines 2390 to 2182
$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) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Comment on lines 1520 to 1524
keys,
keySet: new Set(keys),
numKeys: keys.length,
optionalKeys: new Set(def.optional),
optionalKeys: new Set(keys.filter((key) => def.shape[key].optionality === "optional")),
};
Copy link
Contributor

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.

Suggested change
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";
}),
),
};

Comment on lines 1051 to 1054
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
Copy link
Contributor

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.

Suggested change
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

@colinhacks colinhacks changed the title Deemphasize extend Recursive types in z.object() May 1, 2025
@colinhacks colinhacks changed the title Recursive types in z.object() Support recursive types in z.object(), drop z..interface() May 4, 2025
@colinhacks colinhacks force-pushed the deemphasize-extend branch from 96fde46 to 7c404f7 Compare May 4, 2025 21:15
@colinhacks colinhacks merged commit 681edec into v4 May 4, 2025
3 of 4 checks passed
@colinhacks colinhacks deleted the deemphasize-extend branch May 4, 2025 21:23
@colinhacks colinhacks changed the title Support recursive types in z.object(), drop z..interface() Support recursive types in z.object(), drop z.interface() May 4, 2025
@apfelbox
Copy link
apfelbox commented May 5, 2025

.interface() also solved some other issues, namely the problem with type?: vs. type: ...|undefined among other things. Do these things work now with .object() as well? Because when switching back from .interface() (where everything worked) to .object() I now get a ton of errors in TypeScript.

@apfelbox
Copy link
apfelbox commented May 5, 2025

I found the answer in a thread on twitter, you now have the option to:

Screenshot_2025-05-05_13 50 26

colinhacks added a commit that referenced this pull request May 19, 2025
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0