zod
zod copied to clipboard
superRefine breaks compatibility with discriminatedUnion
Problem
Here's an easy demo, using [email protected]
import * as z from "zod";
const schemaA = z.object({
version: z.literal("a"),
}); // observation, no use of superRefine
z.discriminatedUnion("version", [schemaA]); // works
const schemaB = z
.object({
version: z.literal("b"),
})
.superRefine(() => {}); // observation, use of superRefine
z.discriminatedUnion("version", [schemaB]); // doesn't work
/*
Type 'ZodEffects<ZodObject<{ version: ZodLiteral<"b">; }, "strip", ZodTypeAny, { version: "b"; }, { version: "b"; }>, { version: "b"; }, { version: "b"; }>' is missing the following properties from type 'ZodObject<{ version: ZodTypeAny; } & ZodRawShape, UnknownKeysParam, ZodTypeAny, { [x: string]: any; version?: any; }, { [x: string]: any; version?: any; }>': _cached, _getCached, shape, strict, and 14 more.ts(2740)
*/
The type-error is correct, and it breaks at runtime as well.
In otherwords, ZodEffect<ZodObject> isn't supported... but it should be! A schema that has assertions on top of it is still a compatible schema.
Expectation
z.discriminatedUnion to still work on .superRefined schemas.
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.
ah jamon. have had a patch open for this issue for months
Just hit this bug. Any workaround?
None. I submitted a patch, but it has idled. I ended up just using .union
On Fri, Sep 22, 2023 at 7:26 AM Jonathan Avila @.***> wrote:
Just hit this bug. Any workaround?
— Reply to this email directly, view it on GitHub https://github.com/colinhacks/zod/issues/2440#issuecomment-1731517695, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHU57OLSAKLULUDI7VMMMDX3WNZXANCNFSM6AAAAAAYFOZBP4 . You are receiving this because you authored the thread.Message ID: @.***>
I have ran in to this as well. I was about to submit my own issue with this example: https://codesandbox.io/p/sandbox/suspicious-bash-4z8ptf but found this issue instead.
This will be fixed in Zod 4 per https://github.com/colinhacks/zod/pull/2441#issuecomment-2044094604
Leaving open until then.
I've ran into the same issue, is there a work around?
const SubPartQuestionZodSchema = QuestionSchema.extend({
Type: z.literal(QuestionTypes.SUB_PART),
Questions: z.array(z.lazy(() => QuestionZodSchema)),
});
export const QuestionZodSchema = z.discriminatedUnion("Type", [
FillInTheBlankQuestionZodSchema,
EssayQuestionZodSchema,
SubPartQuestionZodSchema,
]);
wanted to add a superRefine to SubPartQuestionZodSchema
const SubPartQuestionZodSchema = QuestionSchema.extend({
Type: z.literal(QuestionTypes.SUB_PART),
Questions: z.array(z.lazy(() => QuestionZodSchema)),
}).superRefine((data, ctx) => {
if (data.QuestionLevel !== QuestionLevel.SUBJECT) {
if (!data.Metadata.Subjects || data.Metadata.Subjects.length <= 0) {
ctx.addIssue({
code: z.ZodIssueCode.invalid_type,
expected: "array",
received: typeof data.Metadata.Subjects,
message: "Subjects can't be empty",
});
}
}
});
The workaround is to build the ZodDiscriminatedUnion manually :
function zDiscriminatedUnion<T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]]>(key: string, types: T): z.ZodUnion<T>;
function zDiscriminatedUnion(key: string, types: z.ZodTypeAny[]): any {
const optionsMap = new Map();
for(const type of types) {
const value = (type instanceof z.ZodEffects ? type.sourceType() : type).shape[key];
if(!(value instanceof z.ZodLiteral) || optionsMap.has(value.value)) {
throw new Error("cannot contruct discriminated union");
}
optionsMap.set(value.value, type);
}
return new z.ZodDiscriminatedUnion({typeName: z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion, discriminator: key, options: types as any, optionsMap});
}