Skip to content

Commit 2318125

Browse files
committed
fix: allow nested discriminated unions as they are supported in Zod v4
1 parent 34953fa commit 2318125

2 files changed

Lines changed: 98 additions & 7 deletions

File tree

spec/types/discriminated-union.spec.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,102 @@ describe('discriminated union', () => {
251251
},
252252
});
253253
});
254+
255+
it('supports nested discriminated unions', () => {
256+
const Circle = z
257+
.object({
258+
type: z.literal('circle'),
259+
radius: z.number(),
260+
})
261+
.openapi('Circle');
262+
263+
const Rectangle = z
264+
.discriminatedUnion('kind', [
265+
z.object({
266+
type: z.literal('rectangle'),
267+
kind: z.literal('filled'),
268+
color: z.string(),
269+
}),
270+
z.object({
271+
type: z.literal('rectangle'),
272+
kind: z.literal('outlined'),
273+
borderWidth: z.number(),
274+
}),
275+
])
276+
.openapi('Rectangle');
277+
278+
expectSchema(
279+
[z.discriminatedUnion('type', [Circle, Rectangle]).openapi('Shape')],
280+
{
281+
Shape: {
282+
discriminator: {
283+
mapping: {
284+
circle: '#/components/schemas/Circle',
285+
undefined: '#/components/schemas/Rectangle',
286+
},
287+
propertyName: 'type',
288+
},
289+
oneOf: [
290+
{
291+
$ref: '#/components/schemas/Circle',
292+
},
293+
{
294+
$ref: '#/components/schemas/Rectangle',
295+
},
296+
],
297+
},
298+
Circle: {
299+
properties: {
300+
radius: {
301+
type: 'number',
302+
},
303+
type: {
304+
enum: ['circle'],
305+
type: 'string',
306+
},
307+
},
308+
required: ['type', 'radius'],
309+
type: 'object',
310+
},
311+
Rectangle: {
312+
oneOf: [
313+
{
314+
properties: {
315+
color: {
316+
type: 'string',
317+
},
318+
kind: {
319+
enum: ['filled'],
320+
type: 'string',
321+
},
322+
type: {
323+
enum: ['rectangle'],
324+
type: 'string',
325+
},
326+
},
327+
required: ['type', 'kind', 'color'],
328+
type: 'object',
329+
},
330+
{
331+
properties: {
332+
borderWidth: {
333+
type: 'number',
334+
},
335+
kind: {
336+
enum: ['outlined'],
337+
type: 'string',
338+
},
339+
type: {
340+
enum: ['rectangle'],
341+
type: 'string',
342+
},
343+
},
344+
required: ['type', 'kind', 'borderWidth'],
345+
type: 'object',
346+
},
347+
],
348+
},
349+
}
350+
);
351+
});
254352
});

src/transformers/discriminated-union.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,6 @@ export class DiscriminatedUnionTransformer {
7777

7878
const literalValue = value?.def.values[0];
7979

80-
// This should never happen because Zod checks the disciminator type but to keep the types happy
81-
if (typeof literalValue !== 'string') {
82-
throw new Error(
83-
`Discriminator ${discriminator} could not be found in one of the values of a discriminated union`
84-
);
85-
}
86-
8780
mapping[literalValue] = generateSchemaRef(refId);
8881
});
8982

0 commit comments

Comments
 (0)