Invalid type error with discriminatedUnion and or #1761
-
I have the following type: const productItemBase = z.object({
type: z.literal("product"),
title: z.string(),
});
const productItem = productItemBase
.extend({ url: z.string() })
.or(productItemBase.extend({ description: z.string() }));
const contentItem = z.object({
type: z.literal("content"),
title: z.string(),
url: z.string(),
imageUrl: z.string(),
});
const documentItem = z.object({
type: z.literal("document"),
title: z.string(),
url: z.string(),
documentType: z.string(),
});
export const schema = z.object({
items: z.array(
z.discriminatedUnion("type", [productItem, contentItem, documentItem])
),
}); Expected: Actual: |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 4 replies
-
Is this what you are looking for? export const schema = z.object( {
items: z.array(
z.discriminatedUnion( 'type', [
...productItem.options,
contentItem,
documentItem
] )
),
} ) Here's the full example: const productItemBase = z.object( {
type: z.literal( 'product' ),
title: z.string(),
} )
const productItem = productItemBase
.extend( { url: z.string() } )
.or( productItemBase.extend( { description: z.string() } ) )
const contentItem = z.object( {
type: z.literal( 'content' ),
title: z.string(),
url: z.string(),
imageUrl: z.string(),
} )
const documentItem = z.object( {
type: z.literal( 'document' ),
title: z.string(),
url: z.string(),
documentType: z.string(),
} )
export const schema = z.object( {
items: z.array(
z.discriminatedUnion( 'type', [
...productItem.options,
contentItem,
documentItem
] )
),
} ) |
Beta Was this translation helpful? Give feedback.
-
Seemed like a magic solution at first, but then I got this error: const productItemBase = z.object({
type: z.literal('product'),
title: requiredString(),
})
const productItemDetails = z.object({
imageUrl: requiredString(),
})
const productItem = productItemBase
.extend({
url: requiredString('At least one URL required.'),
product: productItemDetails,
})
.or(
productItemBase.extend({
product: productItemDetails.extend({
amazonUrl: requiredString('At least one URL required.'),
}),
})
) So product is in both alternative but with a different schema |
Beta Was this translation helpful? Give feedback.
-
This is marked as answered but I don't think it is. The types validate but when you run the schema (parse) it complains of duplicate values for the discriminator with I expect this is because of the union within the values passed to Because this can be expressed easily in typescript types it seems like a limitation in zod? A more complete example. Is there a way to express this in zod?: type Product = {
type: "product";
} & (
| {
weight: number;
}
| {
quantity: number;
}
);
type Category = {
id: number;
type: "category";
} & ({ title: string } | { parentId: number; subtitle: string });
type UnionOfTypes = Category | Product; |
Beta Was this translation helpful? Give feedback.
-
I did a workaround based on the example above, thought it would help others who would want to achieve the same behavior. const actualItemSchema = z.union( [
productItem,
contentItem,
documentItem,
] )
type ActualItemSchema = z.infer<typeof actualItemSchema>
const itemSchemas = {
product: productItem,
content: contentItem,
document: documentItem,
}
export const schema = z.object({
items: z.array(
z
.any()
.transform(item => item as ActualItemSchema)
.superRefine((item, ctx) => {
const itemType = item.type;
const schema = itemSchemas[itemType];
const result = schema.safeParse(item);
if (!result.success) {
result.error.issues.forEach(issue => {
ctx.addIssue(issue);
});
}
})
),
}); Full example: const productItemBase = z.object( {
type: z.literal( 'product' ),
title: z.string(),
} )
const productItem = productItemBase
.extend( { url: z.string() } )
.or( productItemBase.extend( { description: z.string() } ) )
const contentItem = z.object( {
type: z.literal( 'content' ),
title: z.string(),
url: z.string(),
imageUrl: z.string(),
} )
const documentItem = z.object( {
type: z.literal( 'document' ),
title: z.string(),
url: z.string(),
documentType: z.string(),
} )
const actualItemSchema = z.union( [
productItem,
contentItem,
documentItem,
] )
type ActualItemSchema = z.infer<typeof actualItemSchema>
const itemSchemas = {
product: productItem,
content: contentItem,
document: documentItem,
}
export const schema = z.object({
items: z.array(
z
.any()
.transform(item => item as ActualItemSchema)
.superRefine((item, ctx) => {
const itemType = item.type;
const schema = itemSchemas[itemType];
const result = schema.safeParse(item);
if (!result.success) {
result.error.issues.forEach(issue => {
ctx.addIssue(issue);
});
}
})
),
}); |
Beta Was this translation helpful? Give feedback.
Is this what you are looking for?
Here's the full example: