colinhacks / zod

TypeScript-first schema validation with static type inference

Home Page:https://zod.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using `discriminatedUnion` with schemas typed as ` z.ZodType<MyType>` raise TS error

robmonie opened this issue · comments

We have a fairly complex recursive set of types and schemas that require us to define our types before our schemas and use z.ZodType when defining schemas. This is along the lines of this pattern - https://zod.dev/?id=recursive-types

We've noticed that when we define our schemas this way we get type errors when trying to create a discriminated union on these types.

A simple reproduction

This compiles fine.

const catSchema = z.object({type: z.literal('cat'), scratches: z.boolean()})
const dogSchema = z.object({type: z.literal('dog'), barks: z.boolean()})
const petSchema = z.discriminatedUnion('type', [catSchema, dogSchema]);

type Cat = z.infer<typeof catSchema>;
type Dog = z.infer<typeof dogSchema>;
type Pet = z.infer<typeof petSchema>;

This won't compile and results in the ts error following

type Cat = { type: 'cat'; scratches: boolean;};
type Dog = { type: 'dog'; barks: boolean;};
type Pet = Cat | Dog;

const catSchema: z.ZodType<Cat> = z.object({type: z.literal('cat'), scratches: z.boolean()})
const dogSchema: z.ZodType<Dog>= z.object({type: z.literal('dog'), barks: z.boolean()})
const petSchema: z.ZodType<Pet>  = z.discriminatedUnion('type', [catSchema, dogSchema]);
Type 'ZodType<Cat, ZodTypeDef, Cat>' is missing the following properties from type 'ZodObject<{ type: ZodTypeAny; } & ZodRawShape, UnknownKeysParam, ZodTypeAny, { [x: string]: any; type?: unknown; }, { [x: string]: any; type?: unknown; }>': _cached, _getCached, shape, strict, and 14 more.ts(2740)

Is there a supported way to achieve discriminated unions on schemas defined in this way?

Yeah, unfortunately that won't work as those recursive schemas are types as ZodType, whereas z.discriminatedUnion only accepts object types at the moment. This is one of the fundamental problems I'm aiming to solve with Zod's next major version (coming soon™)

Thanks for replying to quickly Colin.

The reason this has come up for us now is that we faced a sudden and dramatic parsing performance issue on a largish dataset after adding one more type to the existing union of six types (not discriminated). I remembered reading at an earlier time that using a regular union vs discriminated could be quite a bit less performant due to the need to test for each during parsing so I tried moving to discriminated and was reminded of why they weren't initially setup like that.

Interestingly as an experiment I annotated the type errors caused by changing to a discriminated union with @ts-ignore and parsing was very fast but it also worked which I wasn't necessarily expecting. I haven't explored why this is ok or whether it's safe to do in the interim yet but it certainly shows the difference in performance.