Reputation: 2325
Given a discriminated union
export const duParser = z.discriminatedUnion('type', [
z.object({
type: z.literal('a'),
id: aParser,
}),
z.object({
type: z.literal('b'),
id: bParser,
}),
]);
z.infer would be {type: 'a', id: A} | {type: 'b', id: B}
Now, I'd like to procure a parser and type out of it, such as:
The union field (type) is mapped to another name (i.e. type -> type2): {type2: 'a', id: A} | {type2: 'b', id: B}
Other fields (although I agree it's bigger trouble, hence this division in the question) are mapped i.e. {type: 'a', id2: A} | {type: 'b', id2: B}
The specific use case is that I have a generic parser into {type: ..., id: ...}
which I'd like to merge with a flat structure parser, giving the discriminated union prefixed fields. The parser being flat is the requirement of the use case, where the parsed structure is a http query string: rootField1=value1&rootField2=value2&subfieldId=subValue1&subfieldType=a
where subfieldId
and subfieldType
are "id" and "type" of my original discriminatedUnion. Keeping them "id" and "type" isn't desirable since these attributes belong to another level of abstraction (i.e. the root entity could have its own id and type).
Normally, I would do a nested structure here, but I wonder if flattening it together with mapping field names is possible in Zod.
Upvotes: 0
Views: 5872
Reputation: 9836
If I understand the question correctly I think both of the things you're trying to do are possible with the transform
function.
You might need to define the discriminated union type you're trying to transform into, however.
// Using number and string as stand-in types for your a and b parsers
interface IMappedA {
type2: 'a',
id2: number;
}
interface IMappedB {
type2: 'b',
id2: string;
}
type Mapped = IMappedA | IMappedB;
With this type you can perform a transform into this new discriminated union like:
const mappedUnionSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal('a'),
id: z.number(),
}),
z.object({
type: z.literal('b'),
id: z.string(),
}),
]).transform((input): Mapped => {
if (input.type === 'a') {
return { type2: 'a', id2: input.id };
} else {
return { type2: 'b', id2: input.id };
}
});
Upvotes: 2