Skip to content

Commit 66fecce

Browse files
authored
Merge pull request #2159 from Joshua-hypt/feature/zod-handle-array-union-types
2 parents 01134c5 + 99ae4de commit 66fecce

File tree

7 files changed

+214
-42
lines changed

7 files changed

+214
-42
lines changed

.changeset/popular-apples-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hey-api/openapi-ts': patch
3+
---
4+
5+
fix(zod): handle array union types

packages/openapi-ts-tests/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,14 @@ export const zArrayWithProperties = z.array(z.object({
170170
/**
171171
* This is a simple array with any of properties
172172
*/
173-
export const zArrayWithAnyOfProperties = z.array(z.unknown());
173+
export const zArrayWithAnyOfProperties = z.array(z.union([
174+
z.object({
175+
foo: z.string().optional().default('test')
176+
}),
177+
z.object({
178+
bar: z.string().optional()
179+
})
180+
]));
174181

175182
export const zAnyOfAnyAndNull = z.object({
176183
data: z.unknown().optional()
@@ -180,7 +187,14 @@ export const zAnyOfAnyAndNull = z.object({
180187
* This is a simple array with any of properties
181188
*/
182189
export const zAnyOfArrays = z.object({
183-
results: z.array(z.unknown()).optional()
190+
results: z.array(z.union([
191+
z.object({
192+
foo: z.string().optional()
193+
}),
194+
z.object({
195+
bar: z.string().optional()
196+
})
197+
])).optional()
184198
});
185199

186200
/**
@@ -508,7 +522,10 @@ export const zConstValue = z.enum([
508522
*/
509523
export const zCompositionWithNestedAnyOfAndNull = z.object({
510524
propA: z.union([
511-
z.array(z.unknown()),
525+
z.array(z.union([
526+
z3eNum1Период,
527+
zConstValue
528+
])),
512529
z.null()
513530
]).optional()
514531
});
@@ -731,7 +748,10 @@ export const zModelWithAdditionalPropertiesEqTrue = z.object({
731748

732749
export const zNestedAnyOfArraysNullable = z.object({
733750
nullableArray: z.union([
734-
z.array(z.unknown()),
751+
z.array(z.union([
752+
z.string(),
753+
z.boolean()
754+
])),
735755
z.null()
736756
]).optional()
737757
});
@@ -833,7 +853,11 @@ export const zModelWithConstantSizeArray = z.unknown();
833853

834854
export const zModelWithAnyOfConstantSizeArray = z.unknown();
835855

836-
export const zModelWithPrefixItemsConstantSizeArray = z.array(z.unknown());
856+
export const zModelWithPrefixItemsConstantSizeArray = z.array(z.union([
857+
zModelWithInteger,
858+
z.number(),
859+
z.string()
860+
]));
837861

838862
export const zModelWithAnyOfConstantSizeArrayNullable = z.unknown();
839863

packages/openapi-ts-tests/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,14 @@ export const zArrayWithProperties = z.array(z.object({
170170
/**
171171
* This is a simple array with any of properties
172172
*/
173-
export const zArrayWithAnyOfProperties = z.array(z.unknown());
173+
export const zArrayWithAnyOfProperties = z.array(z.union([
174+
z.object({
175+
foo: z.string().optional().default('test')
176+
}),
177+
z.object({
178+
bar: z.string().optional()
179+
})
180+
]));
174181

175182
export const zAnyOfAnyAndNull = z.object({
176183
data: z.union([
@@ -183,7 +190,14 @@ export const zAnyOfAnyAndNull = z.object({
183190
* This is a simple array with any of properties
184191
*/
185192
export const zAnyOfArrays = z.object({
186-
results: z.array(z.unknown()).optional()
193+
results: z.array(z.union([
194+
z.object({
195+
foo: z.string().optional()
196+
}),
197+
z.object({
198+
bar: z.string().optional()
199+
})
200+
])).optional()
187201
});
188202

189203
/**
@@ -486,8 +500,14 @@ export const zCompositionWithAnyOfAnonymous = z.object({
486500
*/
487501
export const zCompositionWithNestedAnyAndTypeNull = z.object({
488502
propA: z.union([
489-
z.array(z.unknown()),
490-
z.array(z.unknown())
503+
z.array(z.union([
504+
zModelWithDictionary,
505+
z.null()
506+
])),
507+
z.array(z.union([
508+
zModelWithArray,
509+
z.null()
510+
]))
491511
]).optional()
492512
});
493513

@@ -503,7 +523,10 @@ export const zConstValue = z.literal('ConstValue');
503523
*/
504524
export const zCompositionWithNestedAnyOfAndNull = z.object({
505525
propA: z.union([
506-
z.array(z.unknown()),
526+
z.array(z.union([
527+
z3eNum1Период,
528+
zConstValue
529+
])),
507530
z.null()
508531
]).optional()
509532
});
@@ -722,7 +745,10 @@ export const zModelWithAdditionalPropertiesEqTrue = z.object({
722745

723746
export const zNestedAnyOfArraysNullable = z.object({
724747
nullableArray: z.union([
725-
z.array(z.unknown()),
748+
z.array(z.union([
749+
z.string(),
750+
z.boolean()
751+
])),
726752
z.null()
727753
]).optional()
728754
});

packages/openapi-ts-tests/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,46 @@
22

33
import * as v from 'valibot';
44

5-
export const vBar = v.union([
5+
export const vContact = v.union([
66
v.object({
7-
bar: v.string()
7+
email: v.string()
88
}),
99
v.object({
10-
baz: v.string()
10+
phone: v.string()
1111
})
1212
]);
1313

14-
export const vFoo = v.intersect([
15-
vBar,
14+
export const vUser = v.intersect([
15+
vContact,
1616
v.object({
17-
foo: v.string()
17+
username: v.string()
1818
})
19-
]);
19+
]);
20+
21+
export const vDogDetails = v.object({
22+
breed: v.string(),
23+
barkVolume: v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(10))
24+
});
25+
26+
export const vCatDetails = v.object({
27+
furLength: v.picklist([
28+
'short',
29+
'medium',
30+
'long'
31+
]),
32+
purrs: v.boolean()
33+
});
34+
35+
export const vPetStore = v.object({
36+
animals: v.array(v.object({
37+
name: v.string(),
38+
type: v.optional(v.picklist([
39+
'dog',
40+
'cat'
41+
])),
42+
details: v.union([
43+
vDogDetails,
44+
vCatDetails
45+
])
46+
}))
47+
});

packages/openapi-ts-tests/test/__snapshots__/3.1.x/validators-union-merge/zod.gen.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,43 @@
22

33
import { z } from 'zod';
44

5-
export const zBar = z.union([
5+
export const zContact = z.union([
66
z.object({
7-
bar: z.string()
7+
email: z.string()
88
}),
99
z.object({
10-
baz: z.string()
10+
phone: z.string()
1111
})
1212
]);
1313

14-
export const zFoo = zBar.and(z.object({
15-
foo: z.string()
16-
}));
14+
export const zUser = zContact.and(z.object({
15+
username: z.string()
16+
}));
17+
18+
export const zDogDetails = z.object({
19+
breed: z.string(),
20+
barkVolume: z.number().int().gte(1).lte(10)
21+
});
22+
23+
export const zCatDetails = z.object({
24+
furLength: z.enum([
25+
'short',
26+
'medium',
27+
'long'
28+
]),
29+
purrs: z.boolean()
30+
});
31+
32+
export const zPetStore = z.object({
33+
animals: z.array(z.object({
34+
name: z.string(),
35+
type: z.enum([
36+
'dog',
37+
'cat'
38+
]).optional(),
39+
details: z.union([
40+
zDogDetails,
41+
zCatDetails
42+
])
43+
}))
44+
});

packages/openapi-ts-tests/test/spec/3.1.x/validators-union-merge.json

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,99 @@
66
},
77
"components": {
88
"schemas": {
9-
"Foo": {
9+
"User": {
1010
"allOf": [
1111
{
12-
"$ref": "#/components/schemas/Bar"
12+
"$ref": "#/components/schemas/Contact"
1313
},
1414
{
1515
"properties": {
16-
"foo": {
16+
"username": {
1717
"type": "string"
1818
}
1919
},
20-
"required": ["foo"],
20+
"required": ["username"],
2121
"type": "object"
2222
}
2323
]
2424
},
25-
"Bar": {
25+
"Contact": {
2626
"oneOf": [
2727
{
2828
"properties": {
29-
"bar": {
29+
"email": {
3030
"type": "string"
3131
}
3232
},
33-
"required": ["bar"],
33+
"required": ["email"],
3434
"type": "object"
3535
},
3636
{
3737
"properties": {
38-
"baz": {
38+
"phone": {
3939
"type": "string"
4040
}
4141
},
42-
"required": ["baz"],
42+
"required": ["phone"],
4343
"type": "object"
4444
}
4545
]
46+
},
47+
"PetStore": {
48+
"type": "object",
49+
"required": ["animals"],
50+
"properties": {
51+
"animals": {
52+
"type": "array",
53+
"items": {
54+
"type": "object",
55+
"required": ["name", "details"],
56+
"properties": {
57+
"name": {
58+
"type": "string"
59+
},
60+
"type": {
61+
"type": "string",
62+
"enum": ["dog", "cat"],
63+
"default": "dog"
64+
},
65+
"details": {
66+
"oneOf": [
67+
{ "$ref": "#/components/schemas/DogDetails" },
68+
{ "$ref": "#/components/schemas/CatDetails" }
69+
]
70+
}
71+
}
72+
}
73+
}
74+
}
75+
},
76+
"DogDetails": {
77+
"type": "object",
78+
"required": ["breed", "barkVolume"],
79+
"properties": {
80+
"breed": {
81+
"type": "string"
82+
},
83+
"barkVolume": {
84+
"type": "integer",
85+
"minimum": 1,
86+
"maximum": 10
87+
}
88+
}
89+
},
90+
"CatDetails": {
91+
"type": "object",
92+
"required": ["furLength", "purrs"],
93+
"properties": {
94+
"furLength": {
95+
"type": "string",
96+
"enum": ["short", "medium", "long"]
97+
},
98+
"purrs": {
99+
"type": "boolean"
100+
}
101+
}
46102
}
47103
}
48104
}

packages/openapi-ts/src/plugins/zod/plugin.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,22 @@ const arrayTypeToZodSchema = ({
9797
// );
9898
}
9999

100-
// TODO: parser - handle union
101-
// return compiler.typeArrayNode(compiler.typeUnionNode({ types: itemExpressions }));
102-
103100
arrayExpression = compiler.callExpression({
104-
functionName,
101+
functionName: compiler.propertyAccessExpression({
102+
expression: zIdentifier,
103+
name: compiler.identifier({ text: 'array' }),
104+
}),
105105
parameters: [
106-
unknownTypeToZodSchema({
107-
context,
108-
schema: {
109-
type: 'unknown',
110-
},
106+
compiler.callExpression({
107+
functionName: compiler.propertyAccessExpression({
108+
expression: zIdentifier,
109+
name: unionIdentifier,
110+
}),
111+
parameters: [
112+
compiler.arrayLiteralExpression({
113+
elements: itemExpressions,
114+
}),
115+
],
111116
}),
112117
],
113118
});

0 commit comments

Comments
 (0)