-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
feat: [#1075] Allows more discriminatedUnion values, use DiscriminatedValue as union element #1589
feat: [#1075] Allows more discriminatedUnion values, use DiscriminatedValue as union element #1589
Conversation
✅ Deploy Preview for guileless-rolypoly-866f8a ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site settings. |
132467c
to
d1aae88
Compare
0503ebf
to
2e60e27
Compare
9ad0944
to
7d0c8b9
Compare
Also adds functionality for disjointed nested discriminators, optional types and other literals. Updates README.md and tests.
…inated-unions-more-types
7d0c8b9
to
021ca61
Compare
Is this PR waiting for a review? I'm looking forward to this being merged. |
@JacobWeisenburger do you have approval privileges? Do you mind looking at this? Thanks! |
I can approve things, but I'm not sure I have enough experience to approve this. I really like the idea, I'm just not sure if there are unintended consequences. |
@JacobWeisenburger understood! I tried to be thorough with the tests to catch any edge cases I could think of. With that said - this is additive so if anything, it should hopefully not impact any existing functionality. Can you think of anything else we can look at in order to merge? Maybe @colinhacks can chime in! |
Hey @maxArturo can I be of any help getting this pr landed? I am looking for a solution to nested discriminated unions for month already and your pr looks like the most complete effort :) |
@Fuzzyma I just have one PR comment to resolve which should happen today or tomorrow (traveling ATM). Otherwise it should be good to go. The main concern is getting someone with merger role to approve or give FB; @scotttrinh @colinhacks anyone else come to mind @JacobWeisenburger ? |
Sounds awesome! Hope your notebook battery doesn't die and you can work 😁 Maybe their discord server has anyone who can approve? |
OK! hopefully more 👀 on this can get it to the finish line. Let me know what else I can do to get this approved! |
Sooo, who is allowed to merge this now? :D |
@Fuzzyma I think Scott and Colin. Here's what I got from @scotttrinh late last week on discord:
I think until then, we'll have to sit tight and wait for these to get pulled into actual package releases. Also, if you're desperate for this you can always install directly from a github repo into your package.json:
|
@maxArturo that is actually what I ended up doing end I found that it doesn't work because the dist files are (rightfully) not in the repo. But you can get around that by adding a prepack script that builds the package. So I had to fork the fork to make that change and install from my fork :D Also: this package gets more and more traction nowadays (because it's awesome!) and the prs will only get more. |
I second this... Zod is an amazing contribution to the TS community, and there are a lot of excellent contributions in the pipeline. It would be a shame to see that pipeline bottlenecked by a busy BDFL. |
This is an amazing PR, just what I would need. Looking forward to this making into a release! 👍 |
@joseph-lozano Do you want to take a look at this and maybe have it merged? @maxArturo There are some conflicts in the branch. |
Wow this is taking long the get merged... |
Hey friends and community members, just wanted to check in here with the latest. Thanks so much to everyone's hard work for getting this PR in shape, it's well done and I appreciate that lots of people from across the community have contributed time and thought to it. Sincerely, thanks! Aside from my own availability for staying on top of reviews and PRs here, I've been a little shy about building atop the existing Apologies for not addressing these dynamics sooner, and I hope people have been able to find ways around the current limitations or maybe used their own fork while the dust is (slowly) settling on this. Thanks for your patience and understanding! Let's keep this open and we'll continue to gather feedback on the proposed changes in #2106 and leave the discussion here for technical or design questions about this change specifically. |
I think there is a separate issue that's been raised here about governance and how we maintain such a popular library, and I'd welcome a lively discussion about this over on the Discord if anyone has the inclination or experience to help us out with that. |
Would love to see this @maxArturo! |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Hey yall. Sorry for having been AFK all these months - lots of personal things were happening. In short - I'm either happy to cleanup/fix this PR or close it, depending on whatever happens with #2106. Since we'd just need this functionality to be integrated in whatever way the repo owners seem fit, we're basically on a holding pattern there. I will say too - a bit of the slowness on these overarching API decisions did cause me a bit to lose focus and switch gears on other tasks. I figure that's the case with other contributors too. But that's another story and no justification to leave people hanging, so I apologize again. There's also https://github.com/teunmooij/zod-discriminated-union which @teunmooij put together in case you need this now 😄. |
I appreciate the work done for this PR by Arturo and all the good discussions. Apologies for the delay on replying. If you've read my opinion of The first comment in this thread is a little out of date, as all of the below have supported as discriminators for a long time now:
Other PRs were merged around the time this one was created, which added support for these things. This PR adds support for The main thrust of this PR is the ability to nest ZodDiscriminatedUnions. My position on this is that it adds to much implementational complexity relative to the value of this API. It's a very niche feature that would increase Zod's unzipped bundle size by ~2kb. Out of the gate, that isn't worth it imo. On top of that, this can be achieved quite simply with const A = z.discriminatedUnion([ ... ]);
const B = z.discriminatedUnion([ ...A.options, ...more options]) On top of that ZodDiscriminatedUnion will probably not exist in its current form in the next major version of Zod for the reasons described here. All together, I wasn't excited about this idea. Even regardless of bundle size considerations, I'm getting increasingly allergic to this kind of visitor-pattern implementation seen in I've just merged this PR that adds support for more types (but not @maxArturo I really appreciate the work you put into this. It's a beautiful approach. 🙌 |
Thanks @colinhacks for taking the time to explain your reasoning. That makes sense to me and I think it’s a good and clean API. Given that it’s one of my first contributions to this repo, I was really surprised by the traction it received by many users of your library! The DU-ish functionality seems to be in high demand. Do you have plans on adding the |
Switch would land in Zod 4, if at all. I'm doing a long-overdue house cleaning right now before starting on Zod 4. I have the advantage of working full-time on Zod for the next couple months. 🤗 At the moment though, I'm leaning toward consolidating everything into Put more generally, ZodUnion can get a lot fancier in terms of runtime optimization, making One of the arguments against ZodSwitch is that it's elements cannot be enumerated, since there's no way for Zod to traverse every code path in the switcher function. This would make ZodSwitch a bit of a headache for every ecosystem tool that attempt to do codegen based on Zod schemas. |
Just my 2c as a new user of Zod: I found nested discriminated unions intriguing, allows me to have domain logic in types: For example a CRUD form object with or without an id property (create/update), but then nested are the "actual" discriminated unions, certain properties depending on the object kind. I have used the forks https://github.com/teunmooij/zod-discriminated-union and https://github.com/parisholley/zod-discriminated-union and it nesting is one major feature I see actually. |
That pattern does not work. TypeScript accepts it at compile time, but Zod rejects it at runtime with import { z } from "zod";
const stream_event_schema = z.discriminatedUnion("op", [
z.object({ type: z.literal("stream"), op: z.literal("create") }),
z.object({ type: z.literal("stream"), op: z.literal("update") }),
z.object({ type: z.literal("stream"), op: z.literal("delete") }),
]);
const event_schema = z.discriminatedUnion("type", [
z.object({ type: z.literal("message") }),
z.object({ type: z.literal("presence") }),
...stream_event_schema.options,
]);
Moreover, even if it did work, it would lose all information about the inner discriminator, negating all the optimization and diagnostic advantages of the inner |
Partially closes #1075 , fully closes #1884 with the following:
feature: more valid discriminator values
The following are now well-supported discriminator values (enum/undefined could have been already)
z.undefined()
z.null()
z.enum()
z.discriminatedUnion()
This does not address functionality like the below, where a full primitive validator (like
z.string()
) is used as discriminator. This requires more changes and I'd like to address it separately.feature: use
ZodDiscriminatedUnion
as a union elementAllows adding a
ZodDiscriminatedUnion
as an element in the discriminated union, such that this is possible:Benchmarks for the new functionality (compared to two and four union elements, where
nested
is one of the options ofDiscriminatedUnion
itself):$ yarn benchmark yarn run v1.22.19 $ tsx src/benchmarks/index.ts z.discriminatedUnion: double: valid: a x 994,112 ops/sec ±0.91% (85 runs sampled) z.discriminatedUnion: double: valid: b x 964,468 ops/sec ±0.98% (85 runs sampled) z.discriminatedUnion: double: invalid: null x 31,516 ops/sec ±1.10% (80 runs sampled) z.discriminatedUnion: double: invalid: wrong shape x 34,323 ops/sec ±1.37% (81 runs sampled) z.discriminatedUnion: many: valid: a x 948,977 ops/sec ±0.89% (87 runs sampled) z.discriminatedUnion: many: valid: c x 944,571 ops/sec ±0.85% (85 runs sampled) z.discriminatedUnion: many: invalid: null x 29,904 ops/sec ±1.24% (87 runs sampled) z.discriminatedUnion: many: invalid: wrong shape x 32,622 ops/sec ±1.16% (88 runs sampled) z.discriminatedUnion: nested: valid: a x 961,652 ops/sec ±0.82% (85 runs sampled) z.discriminatedUnion: nested: valid: b x 963,830 ops/sec ±0.96% (86 runs sampled) z.discriminatedUnion: nested: valid: c x 956,284 ops/sec ±0.88% (84 runs sampled) z.discriminatedUnion: nested: invalid: null x 29,937 ops/sec ±1.19% (87 runs sampled) z.discriminatedUnion: nested: invalid: wrong shape x 964,482 ops/sec ±0.91% (86 runs sampled) ✨ Done in 71.60s.
feature: disjointed discriminators for nested DUs
Now the following is possible:
Care was taken to ensure that there aren't any edge cases where nested DUs can be created without overlapping, resolvable discriminators at every level.
Follow-ups
DiscriminatedUnion
elements:z.intersection()
z.string()
z.boolean()