Skip to content
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

Discriminated Union based on nested object property? #1868

Open
sharedRoutine opened this issue Jan 12, 2023 · 5 comments
Open

Discriminated Union based on nested object property? #1868

sharedRoutine opened this issue Jan 12, 2023 · 5 comments
Labels
enhancement New feature or request

Comments

@sharedRoutine
Copy link

sharedRoutine commented Jan 12, 2023

Hi there!

I've been playing around with Zod a lot lately and I've been defining a lot of different schemas. When I was reading up on discriminatedUnions, it got me thinking if the following was possible to define in zod:

{
   subscription: {
      type: 'typeA' | 'typeB'
  },
  event: {
      id: 1234 // only present in typeA subscriptions
      name: "abc"
  }
}

I could make this property option and use refine to check if type is typeA and then enforce id to be present, which is what I am currently going with.
However I'd like to define a discriminatedUnion based on the subscription type and then define the event object accordingly.

Is there any way this can be achieved?

e.g

z.discriminatedUnion("subscription.type", [
  z.object({ 
    subscription: z.object({ 
      type: z.literal("typeA") 
    }) 
  }),
  z.object({ 
    subscription: z.object({ 
      type: z.literal("typeB") 
    }) 
  }),
])

Thanks!

@JacobWeisenburger JacobWeisenburger added the enhancement New feature or request label Jan 14, 2023
@DNoel26
Copy link

DNoel26 commented Jan 24, 2023

I find using superRefine for these more complicated use cases to work well so far. Maybe try something like

z
    .object({
        subscription: z.object(YourZodSchema).superRefine((val, ctx) => {
            if (val.type === 'typeA") {
                // do something
                // can add issue via ctx.addIssue
                // or can z.something().parse(val)
            } else {
                // do other thing
                // can other add issue via ctx.addIssue
                // or can other z.something().parse(val)
            }
        })
    })

sergei-maertens added a commit to open-formulieren/formio-builder that referenced this issue Jan 8, 2024
The fixedValue validation in the zod schema was not
incorporated yet.

Doing this via the union in zod is not possible because we have nested
descriminators, and zod can't figure this out (nor can typescript, to
be fair). See colinhacks/zod#1868 for more
information.
sergei-maertens added a commit to open-formulieren/formio-builder that referenced this issue Jan 8, 2024
The fixedValue validation in the zod schema was not
incorporated yet.

Doing this via the union in zod is not possible because we have nested
descriminators, and zod can't figure this out (nor can typescript, to
be fair). See colinhacks/zod#1868 for more
information.
@tazmaniax
Copy link

tazmaniax commented Feb 23, 2024

I need the same nested object property as a discriminator functionality but I get the impression it's still not supported despite all of the discussions in #2106 , yes?

@0xdevalias
Copy link

0xdevalias commented Dec 5, 2024

I was hoping to be able to do something like this:

const statusQuerySchema = z.discriminatedUnion("customStatus.stage", [
  statusQueryFooResultSuccessSchema,
  statusQueryBarResultSuccessSchema,
]);

I'm not sure if this is a good way to achieve it, but I was playing around this afternoon and came up with this idea as a hacky workaround; basically using preprocess to create a root level key that I could then use as my discriminator:

z.preprocess(
  (input) => {
    if (typeof input !== "object" || input === null) return input;

    const result = z
      .object({
        customStatus: customStatusSchema,
      })
      .safeParse(input);

    if (result.success) {
      return {
        ...input,
        _flattenedCustomStatusStage: result.data.customStatus.stage,
        _flattenedCustomStatusStatus: result.data.customStatus.status,
      };
    }
  },
  z.discriminatedUnion("_flattenedCustomStatusStage", [
    statusQueryFooResultSuccessSchema,
    statusQueryBarResultSuccessSchema,
  ]),
);

@jonnysamps
Copy link

@0xdevalias thats a good workaround until there is first-class support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants