zod
zod copied to clipboard
Nested discriminated unions
I've got an object that has discriminated unions within discriminated unions:
[
{
"_id": "98439752-0cd4-406b-982f-1bbf1df5480d",
"type": "data",
"subtype": "subtype-1",
"data": {
"id": "a8d44d3f-c18a-4178-990c-f543ab8f3a8d",
"url": "https://www.google.com",
"title": {
"en": "English title"
}
}
},
{
"_id": "4f674fe7-368f-4b15-924b-7e61dd982961",
"type": "data",
"subtype": "subtype-2",
"data": {
"id": "160a6427-ec74-4488-bceb-8cebac86b55f",
"path": "/",
"description": "this is another subtype"
}
},
{
"_id": "aba34b25-8a33-4046-a62f-c5ff9a6befef",
"type": "reference",
"referent": {
"id": "b940a1e8-3733-425f-b1ef-667c2ecdab04",
"type": "document"
}
},
{
"_id": "aba34b25-8a33-4046-a62f-c5ff9a6befef",
"type": "reference",
"referent": {
"id": "b940a1e8-3733-425f-b1ef-667c2ecdab04",
"type": "file"
}
}
]
On the first level the type discriminator is the field type. For type: 'data', we can further discriminate the type by the field subtype.
When I try to do this with nested usage of discriminatedUnion like this:
import { z } from 'zod';
import data from './data.json';
const ReferenceSchema = z.object({
_id: z.string(),
type: z.literal('reference'),
referent: z.object({
id: z.string(),
type: z.enum(['document', 'file']),
}),
});
const Subtype1Schema = z.object({
_id: z.string(),
type: z.literal('data'),
subtype: z.literal('subtype-1'),
data: z.object({
id: z.string(),
url: z.string().url(),
title: z.object({
en: z.string(),
}),
}),
});
const Subtype2Schema = z.object({
_id: z.string(),
type: z.literal('data'),
subtype: z.literal('subtype-2'),
data: z.object({
id: z.string(),
path: z.string(),
description: z.string(),
}),
});
const DataSchemma = z.discriminatedUnion('subtype', [
Subtype1Schema,
Subtype2Schema,
]);
export const Schema = z.array(
z.discriminatedUnion('type', [ReferenceSchema, DataSchemma])
);
const rc = Schema.parse(data);
I get an error saying:
Error: The discriminator value could not be extracted from all the provided schemas
How can I achieve that once my code checks if type === 'data' TypeScript knows that now, there's a field subtype and an object called data?
You can play with this example here: https://stackblitz.com/edit/typescript-piewve?devToolsHeight=33&file=index.ts
There's an open PR for this @gligoran https://github.com/colinhacks/zod/pull/1589. The functionality will hopefully also be upgraded with intersections and other Zod types that make sense.
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.