zod
zod copied to clipboard
`discriminatedUnion` produces TS error when `.default` or `.preprocess` are applied
Given this snippet:
import { z } from 'zod';
const FooSchema = z.object({
type: z.literal('foo').default('foo'),
a: z.string(),
});
const BarSchema = z.object({
type: z.literal('custom'),
method: z.string(),
});
const BazSchema = z.discriminatedUnion('type', [FooSchema, BarSchema]);
console.log(BazSchema.parse({ a: 'foo' }));
TypeScript produces this error:
$ tsc tmp.ts
tmp.ts:13:49 - error TS2322: Type 'ZodObject<{ type: ZodDefault<ZodLiteral<"foo">>; a: ZodString; }, "strip", ZodTypeAny, { type?: "foo"; a?: string; }, { type?: "foo"; a?: string; }>' is not assignable to type 'ZodDiscriminatedUnionOption<"type", Primitive>'.
Type '{ type: z.ZodDefault<z.ZodLiteral<"foo">>; a: z.ZodString; }' is not assignable to type '{ type: ZodLiteral<Primitive>; } & ZodRawShape'.
Type '{ type: z.ZodDefault<z.ZodLiteral<"foo">>; a: z.ZodString; }' is not assignable to type '{ type: ZodLiteral<Primitive>; }'.
Types of property 'type' are incompatible.
Property 'value' is missing in type 'ZodDefault<ZodLiteral<"foo">>' but required in type 'ZodLiteral<Primitive>'.
13 const BazSchema = z.discriminatedUnion('type', [FooSchema, BarSchema]);
~~~~~~~~~
node_modules/zod/lib/types.d.ts:531:9
531 get value(): T;
~~~~~
'value' is declared here.
Found 1 error in tmp.ts:13
This code works at runtime however and Zod correctly parses the object:
{ type: 'foo', a: 'foo' }
Previously opened as #1263 but was closed as stale.
Seems like there is a same problem if you refine one of the object schemas inside a union as well:
z.discriminatedUnion('type', [
z.strictObject({
type: z.literal('foo'),
}),
z.strictObject({
type: z.literal('bar'),
}).refine((input) => true, 'Error'),
]);
This one produces an error:
Type 'ZodEffects<ZodObject<{ type: ZodLiteral<"bar">; }, "strict", ZodTypeAny, { type: "bar"; }, { type: "bar"; }>, { type: "bar"; }, { type: "bar"; }>' is missing the following properties from type 'ZodObject<{ type: ZodLiteral<Primitive>; } & ZodRawShape, any, any, { [x: string]: any; }, { [x: string]: any; }>': _cached, _getCached, shape, strict, and 14 more.
'input' is declared but its value is never read.
And here's an ugly workaround for the refinement problem:
const bar =
z.strictObject({
type: z.literal('bar'),
}).refine((input) => true, 'Error');
type ExtractRefinementType<T extends z.ZodTypeAny> = T extends z.ZodEffects<
infer T,
any,
any
>
? ExtractRefinementType<T>
: T;
z.discriminatedUnion('type', [
z.strictObject({
type: z.literal('foo'),
}),
bar as unknown as ExtractRefinementType<typeof bar>
]);
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.
This would still be very nice to have!
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.
@colinhacks I believe this may be marked as good first issue.
Hey @RobertCraigie 👋 When I try the exact same approach you've mentioned I receive a runtime zod error of:
ZodError: [
{
"code": "invalid_union_discriminator",
"options": [
"foo",
"custom"
],
"path": [
"type"
],
"message": "Invalid discriminator value. Expected 'foo' | 'custom'"
}
]
I was trying to default to a specific zod object if the type
(in your example) is undefined, but it doesn't seem to be straightforward. Sadly z.discriminatedUnion
doesn't seem to work, but the normal z.union
seems to work fine.
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.
Not stale
Still seeing this error when trying to apply a .default
to discriminatedUnion as of v3.22.4
.
Still happening with refine too