Skip to content

Commit 4b17788

Browse files
authored
fix(zod): fixed missing oneOf and common property combinations for zod (orval-labs#2672)
1 parent 4a687bd commit 4b17788

File tree

3 files changed

+361
-133
lines changed

3 files changed

+361
-133
lines changed

packages/zod/src/index.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@ export const generateZodValidationSchemaDefinition = (
254254
),
255255
);
256256

257-
// Handle allOf with additional properties - merge additional properties into the last schema
258-
if (schema.allOf && schema.properties) {
257+
// Handle allOf/oneOf/anyOf with additional properties
258+
if (schema.properties && Object.keys(schema.properties).length > 0) {
259259
const additionalPropertiesSchema = {
260260
properties: schema.properties,
261261
required: schema.required,
@@ -277,10 +277,24 @@ export const generateZodValidationSchemaDefinition = (
277277
},
278278
);
279279

280-
baseSchemas.push(additionalPropertiesDefinition);
280+
// For oneOf/anyOf, use allOf to combine union with common properties
281+
// This generates: zod.union([...]).and(commonProperties)
282+
if (schema.oneOf || schema.anyOf) {
283+
functions.push([
284+
'allOf',
285+
[
286+
{ functions: [[separator, baseSchemas]], consts: [] },
287+
additionalPropertiesDefinition,
288+
],
289+
]);
290+
} else {
291+
// For allOf, just add to the list
292+
baseSchemas.push(additionalPropertiesDefinition);
293+
functions.push([separator, baseSchemas]);
294+
}
295+
} else {
296+
functions.push([separator, baseSchemas]);
281297
}
282-
283-
functions.push([separator, baseSchemas]);
284298
skipSwitchStatement = true;
285299
}
286300

samples/hono/hono-with-zod/src/petstore.zod.ts

Lines changed: 187 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,53 @@ export const ListPetsQueryParams = zod.object({
1616

1717
export const ListPetsResponseItem = zod.preprocess(
1818
stripNill,
19-
zod.union([
20-
zod.union([
19+
zod
20+
.union([
21+
zod
22+
.union([
23+
zod
24+
.object({
25+
cuteness: zod.number(),
26+
breed: zod.enum(['Labradoodle']),
27+
})
28+
.strict(),
29+
zod
30+
.object({
31+
length: zod.number(),
32+
breed: zod.enum(['Dachshund']),
33+
})
34+
.strict(),
35+
])
36+
.and(
37+
zod
38+
.object({
39+
barksPerMinute: zod.number().optional(),
40+
type: zod.enum(['dog']),
41+
})
42+
.strict(),
43+
),
2144
zod
2245
.object({
23-
cuteness: zod.number(),
24-
breed: zod.enum(['Labradoodle']),
46+
petsRequested: zod.number().optional(),
47+
type: zod.enum(['cat']),
2548
})
2649
.strict(),
50+
])
51+
.and(
2752
zod
2853
.object({
29-
length: zod.number(),
30-
breed: zod.enum(['Dachshund']),
54+
'@id': zod.string().optional(),
55+
id: zod.number(),
56+
name: zod.string(),
57+
tag: zod.string().optional(),
58+
email: zod.string().email().optional(),
59+
callingCode: zod.enum(['+33', '+420', '+33']).optional(),
60+
country: zod
61+
.enum(["People's Republic of China", 'Uruguay'])
62+
.optional(),
3163
})
3264
.strict(),
33-
]),
34-
zod
35-
.object({
36-
petsRequested: zod.number().optional(),
37-
type: zod.enum(['cat']),
38-
})
39-
.strict(),
40-
]),
65+
),
4166
);
4267
export const ListPetsResponse = zod.array(ListPetsResponseItem);
4368

@@ -49,71 +74,140 @@ export const CreatePetsBody = zod.array(CreatePetsBodyItem);
4974

5075
export const CreatePetsResponse = zod.preprocess(
5176
stripNill,
52-
zod.union([
53-
zod.union([
77+
zod
78+
.union([
79+
zod
80+
.union([
81+
zod
82+
.object({
83+
cuteness: zod.number(),
84+
breed: zod.enum(['Labradoodle']),
85+
})
86+
.strict(),
87+
zod
88+
.object({
89+
length: zod.number(),
90+
breed: zod.enum(['Dachshund']),
91+
})
92+
.strict(),
93+
])
94+
.and(
95+
zod
96+
.object({
97+
barksPerMinute: zod.number().optional(),
98+
type: zod.enum(['dog']),
99+
})
100+
.strict(),
101+
),
54102
zod
55103
.object({
56-
cuteness: zod.number(),
57-
breed: zod.enum(['Labradoodle']),
104+
petsRequested: zod.number().optional(),
105+
type: zod.enum(['cat']),
58106
})
59107
.strict(),
108+
])
109+
.and(
60110
zod
61111
.object({
62-
length: zod.number(),
63-
breed: zod.enum(['Dachshund']),
112+
'@id': zod.string().optional(),
113+
id: zod.number(),
114+
name: zod.string(),
115+
tag: zod.string().optional(),
116+
email: zod.string().email().optional(),
117+
callingCode: zod.enum(['+33', '+420', '+33']).optional(),
118+
country: zod
119+
.enum(["People's Republic of China", 'Uruguay'])
120+
.optional(),
64121
})
65122
.strict(),
66-
]),
67-
zod
68-
.object({
69-
petsRequested: zod.number().optional(),
70-
type: zod.enum(['cat']),
71-
})
72-
.strict(),
73-
]),
123+
),
74124
);
75125

76-
export const UpdatePetsBody = zod.union([
77-
zod.union([
126+
export const UpdatePetsBody = zod
127+
.union([
128+
zod
129+
.union([
130+
zod.object({
131+
cuteness: zod.number(),
132+
breed: zod.enum(['Labradoodle']),
133+
}),
134+
zod.object({
135+
length: zod.number(),
136+
breed: zod.enum(['Dachshund']),
137+
}),
138+
])
139+
.and(
140+
zod.object({
141+
barksPerMinute: zod.number().optional(),
142+
type: zod.enum(['dog']),
143+
}),
144+
),
78145
zod.object({
79-
cuteness: zod.number(),
80-
breed: zod.enum(['Labradoodle']),
146+
petsRequested: zod.number().optional(),
147+
type: zod.enum(['cat']),
81148
}),
149+
])
150+
.and(
82151
zod.object({
83-
length: zod.number(),
84-
breed: zod.enum(['Dachshund']),
152+
'@id': zod.string().optional(),
153+
id: zod.number(),
154+
name: zod.string(),
155+
tag: zod.string().optional(),
156+
email: zod.string().email().optional(),
157+
callingCode: zod.enum(['+33', '+420', '+33']).optional(),
158+
country: zod.enum(["People's Republic of China", 'Uruguay']).optional(),
85159
}),
86-
]),
87-
zod.object({
88-
petsRequested: zod.number().optional(),
89-
type: zod.enum(['cat']),
90-
}),
91-
]);
160+
);
92161

93162
export const UpdatePetsResponse = zod.preprocess(
94163
stripNill,
95-
zod.union([
96-
zod.union([
164+
zod
165+
.union([
166+
zod
167+
.union([
168+
zod
169+
.object({
170+
cuteness: zod.number(),
171+
breed: zod.enum(['Labradoodle']),
172+
})
173+
.strict(),
174+
zod
175+
.object({
176+
length: zod.number(),
177+
breed: zod.enum(['Dachshund']),
178+
})
179+
.strict(),
180+
])
181+
.and(
182+
zod
183+
.object({
184+
barksPerMinute: zod.number().optional(),
185+
type: zod.enum(['dog']),
186+
})
187+
.strict(),
188+
),
97189
zod
98190
.object({
99-
cuteness: zod.number(),
100-
breed: zod.enum(['Labradoodle']),
191+
petsRequested: zod.number().optional(),
192+
type: zod.enum(['cat']),
101193
})
102194
.strict(),
195+
])
196+
.and(
103197
zod
104198
.object({
105-
length: zod.number(),
106-
breed: zod.enum(['Dachshund']),
199+
'@id': zod.string().optional(),
200+
id: zod.number(),
201+
name: zod.string(),
202+
tag: zod.string().optional(),
203+
email: zod.string().email().optional(),
204+
callingCode: zod.enum(['+33', '+420', '+33']).optional(),
205+
country: zod
206+
.enum(["People's Republic of China", 'Uruguay'])
207+
.optional(),
107208
})
108209
.strict(),
109-
]),
110-
zod
111-
.object({
112-
petsRequested: zod.number().optional(),
113-
type: zod.enum(['cat']),
114-
})
115-
.strict(),
116-
]),
210+
),
117211
);
118212

119213
export const ShowPetByIdParams = zod.object({
@@ -123,26 +217,51 @@ export const ShowPetByIdParams = zod.object({
123217

124218
export const ShowPetByIdResponse = zod.preprocess(
125219
stripNill,
126-
zod.union([
127-
zod.union([
220+
zod
221+
.union([
222+
zod
223+
.union([
224+
zod
225+
.object({
226+
cuteness: zod.number(),
227+
breed: zod.enum(['Labradoodle']),
228+
})
229+
.strict(),
230+
zod
231+
.object({
232+
length: zod.number(),
233+
breed: zod.enum(['Dachshund']),
234+
})
235+
.strict(),
236+
])
237+
.and(
238+
zod
239+
.object({
240+
barksPerMinute: zod.number().optional(),
241+
type: zod.enum(['dog']),
242+
})
243+
.strict(),
244+
),
128245
zod
129246
.object({
130-
cuteness: zod.number(),
131-
breed: zod.enum(['Labradoodle']),
247+
petsRequested: zod.number().optional(),
248+
type: zod.enum(['cat']),
132249
})
133250
.strict(),
251+
])
252+
.and(
134253
zod
135254
.object({
136-
length: zod.number(),
137-
breed: zod.enum(['Dachshund']),
255+
'@id': zod.string().optional(),
256+
id: zod.number(),
257+
name: zod.string(),
258+
tag: zod.string().optional(),
259+
email: zod.string().email().optional(),
260+
callingCode: zod.enum(['+33', '+420', '+33']).optional(),
261+
country: zod
262+
.enum(["People's Republic of China", 'Uruguay'])
263+
.optional(),
138264
})
139265
.strict(),
140-
]),
141-
zod
142-
.object({
143-
petsRequested: zod.number().optional(),
144-
type: zod.enum(['cat']),
145-
})
146-
.strict(),
147-
]),
266+
),
148267
);

0 commit comments

Comments
 (0)