Skip to content
This repository was archived by the owner on Nov 19, 2024. It is now read-only.

Support for union or discriminatedUnion? #255

Open
jackwlee01 opened this issue Feb 1, 2023 · 12 comments
Open

Support for union or discriminatedUnion? #255

jackwlee01 opened this issue Feb 1, 2023 · 12 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@jackwlee01
Copy link

jackwlee01 commented Feb 1, 2023

The following code produces a runtime error:

  thing: publicProcedure
    .meta({ openapi: { method: 'GET', path: '/api/thing' }})
    .input( z.union([
      z.object({ type: z.literal('first'), myVar: z.string() }),
      z.object({ type: z.literal('another'), anotherVar: z.number() }),
      z.object({ type: z.literal('third') }),
    ]))
    .output( z.string() )
    .query(({ ctx }) => "thing" ),

TRPCError: [query.thing] - Input parser must be a ZodObject

Is there any planned support for union or discriminatedUnion?

@jlalmes jlalmes added the enhancement New feature or request label Feb 3, 2023
@stale
Copy link

stale bot commented Apr 4, 2023

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.

@stale stale bot added the stale label Apr 4, 2023
@stale stale bot closed this as completed Apr 11, 2023
@jlalmes jlalmes added help wanted Extra attention is needed and removed stale labels May 24, 2023
@jlalmes jlalmes reopened this May 24, 2023
@levi
Copy link

levi commented Jun 2, 2023

I looked into this. Looks like a non-trivial change since zod-to-json-schema simply returns an anyOf set whenever it sees a union with the openapi3 option.

To properly support OpenAPI3 style discriminators, one would have to either modify zod-to-json-schema to support an openapi3 specific output (which would be a significant change for that package) or perform some pre/post processing in zodSchemaToOpenApiSchemaObject.

I think the latter would be better for this library, specifically. However, I still need to investigate further what a preprocessing or post processing step would look like in practice. Open to ideas!

@ThePaulMcBride
Copy link

This would be a great feature. Unions are extremely useful for creating strict contracts and type narrowing. I definitely miss having this ability when working with openapi.

@realjesset
Copy link

what happened this support? how can I tackle a discriminatedUnion without using it?

@levi
Copy link

levi commented Jun 15, 2023

I wrote a hacky implementation to start a discussion, but realized that my needs were for nested discriminated unions, which are not supported in the OpenAPI 3 spec. I closed my PR, but someone feel free to take a look at the kernel of the PR and open a new one.

@baptisteArno
Copy link

I don't understand the challenge since zod-to-json-schema correctly parses unions? anyOf isn't what we want?

@ntindle
Copy link

ntindle commented Sep 20, 2023

This is also triggered by z.array()

@popuguytheparrot
Copy link

+1

@hoangtrung99
Copy link

hoangtrung99 commented Nov 5, 2023

I cannot use this library just because it lacks this feature. This is a necessary thing, but it seems like the library has not been functioning actively recently, could it be that it has died?

@sebinsua
Copy link

sebinsua commented Dec 11, 2023

I wrote some logic to handle .input({ appId: z.union([z.number(), z.string()]) }) as originally (1) this would error about the union not being coercible (when technically everything within it is coercible), and additionally I (2) wrote some logic to ensure that the values within the input that is produced are coerced into the correct type. I think this approach is fine with the zod-to-json-schema anyOf set mentioned above...?

Is there something that I'm misunderstanding about this requirement or is my situation a little different/simpler? I can make the PR if what I've done locally is somewhat beneficial.

@ferdy-roz
Copy link

+1

@tsumo
Copy link

tsumo commented Mar 29, 2024

This issue could be hacked around like this

const unionSchema = z.discriminatedUnion('type', [
    z.object({ type: z.literal('number'), number: z.number() }),
    z.object({ type: z.literal('string'), string: z.string() }),
]);

const restSchema = z.object({
    type: z.string(),
    number: z.number().optional(),
    string: z.string().optional(),
});

export const restRouter = router({
    test: procedure
        .meta({
            openapi: {
                method: 'GET',
                path: '/test',
            },
        })
        .input(restSchema)
        .output(z.object({}))
        .query(async ({ input }) => {
            const validatedInput = await unionSchema.parseAsync(input);
            return {};
        }),
})

The code is still safe, just with a bit of duplication

# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests