colinhacks on X: "whoa. I just found a way to properly infer recursive types in z.object() — no casting, no z.lazy(), no scopes/registries, no special syntax.
i've been trying to do this for literally years"
whoa. I just found a way to properly infer recursive types in z.object() — no casting, no z.lazy(), no scopes/registries, no special syntax.
i've been trying to do this for literally years
whoa. I just found a way to properly infer recursive types in z.object() — no casting, no z.lazy(), no scopes/registries, no special syntax.
i've been trying to do this for literally years
this development kind of negates the raison d'être for `z.interface()`. it has been removed in the most recent betas. i've never been happier to delete 3k LOC.
"but what about key-optional versus value-optional properties?"
this was one of two stated reasons for the introduction of z.interface() in the original Zod 4 beta announcement (the other being recursive type support)
at that point I was trying to steer people towards z.interface() so I chose de-emphasized a minor change coming in Zod 4...
Zod now treats `.optional()` slightly differently than `.or(z.undefined()` — which means both kinds of optionality can be represented in z.object() 🍾
to be clear: there is more to this than just the usage of the getter—this will only work in Zod 4. the getter is table stakes when defining self-referential objects (though more libraries should use it instead of a dedicated "lazy" callback API imo). the hard part was getting