Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

yz89122
Copy link
Contributor

@yz89122 yz89122 commented Jul 31, 2025

Summary

This PR fixes ReferenceError: Cannot access before initialization errors that occur when using methods like .check(), .catchall(), .passthrough(), etc. on recursive object schemas.

Problem

When defining recursive objects and using certain methods, the object spread syntax ({...shape}) causes initialization errors:

const Category = z.object({
  id: z.string(),
  get subcategories() {
    return z.array(Category).optional();
  },
}).check((ctx) => {
  // Custom validation logic
});
// ReferenceError: Cannot access 'Category' before initialization

The issue occurs because object spread syntax immediately evaluates all properties, including getters. When the getter references a recursive type that's still being initialized, it triggers a ReferenceError.

Solution

Replace object spread with a new util.objectClone() function that uses Object.create() with Object.getOwnPropertyDescriptors() to properly clone objects while preserving property descriptors (including getters) without evaluating them immediately.

This approach:

  • Preserves all property descriptors exactly as they are
  • Defers getter evaluation until after initialization is complete
  • Maintains the same behavior for non-recursive objects
  • Works with the existing shape getter pattern

Changes

  • Added util.objectClone() function that properly clones objects with descriptors
  • Updated shape getter implementations in object(), strictObject(), and looseObject() to use objectClone() instead of object spread
  • Added test case demonstrating recursive objects with .check() method

Testing

All existing tests pass, plus new test case for recursive objects with .check() method.

🤖 Generated with Claude Code

yz89122 and others added 2 commits August 2, 2025 05:20
…thods

Replace object spread syntax with util.objectClone() to properly clone objects
with property descriptors. This prevents immediate evaluation of getters during
object cloning, which was causing ReferenceError when the getter referenced
a recursive type that was still being initialized.

The new objectClone function uses Object.create() with Object.getOwnPropertyDescriptors()
to preserve all property descriptors exactly as they are, deferring getter evaluation
until after initialization is complete.

🤖 Generated with [Claude Code](https://claude.ai/code)
Tests that recursive object schemas can use .check() method without causing
ReferenceError. The test validates duplicate ID detection among sibling
subcategories in a recursive category structure.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@yz89122
Copy link
Contributor Author

yz89122 commented Aug 2, 2025

I ran the tests with the latest typescript (5.9.2) on the main branch, and they failed in the same way. As you can see the GitHub Actions of the last merged commit https://github.com/colinhacks/zod/actions/runs/16682585688/job/47224521400.

cc @colinhacks

An issue #5035 and a PR #5033 created.

@yz89122 yz89122 marked this pull request as ready for review August 4, 2025 03:57
@colinhacks colinhacks merged commit d43cf19 into colinhacks:main Aug 4, 2025
2 of 4 checks passed
@yz89122 yz89122 deleted the fix/recursive-object-initialization branch August 5, 2025 08:27
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