fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #2736 from hey-api/copilot/fix-2110710e-8174-470d-8162-ec3f601f9ba9

authored by

Lubos and committed by
GitHub
d79dc9d1 c7f3d53b

+656 -60
+5
.changeset/mighty-paws-design.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + fix(zod): allOf in array items being generated as union instead of intersection
+40
packages/openapi-ts-tests/specs/2.0.x/array-items-all-of.yaml
··· 1 + swagger: '2.0' 2 + info: 3 + title: OpenAPI 2.0 array items allOf example 4 + version: '1' 5 + definitions: 6 + ArrayWithAllOfObjects: 7 + type: array 8 + items: 9 + allOf: 10 + - type: object 11 + properties: 12 + id: 13 + type: integer 14 + - type: object 15 + properties: 16 + name: 17 + type: string 18 + ArrayWithAllOfPrimitives: 19 + type: array 20 + items: 21 + allOf: 22 + - type: number 23 + - type: string 24 + ArrayWithAllOfRefs: 25 + type: array 26 + items: 27 + allOf: 28 + - $ref: '#/definitions/BaseModel' 29 + - type: object 30 + properties: 31 + extra: 32 + type: string 33 + BaseModel: 34 + type: object 35 + properties: 36 + id: 37 + type: integer 38 + createdAt: 39 + type: string 40 + format: date-time
+44
packages/openapi-ts-tests/specs/3.0.x/array-items-all-of.yaml
··· 1 + openapi: 3.0.2 2 + info: 3 + title: OpenAPI 3.0.2 array items allOf example 4 + version: '1' 5 + components: 6 + schemas: 7 + # Test case 1: Array with allOf of object schemas 8 + ArrayWithAllOfObjects: 9 + type: array 10 + items: 11 + allOf: 12 + - type: object 13 + properties: 14 + id: 15 + type: integer 16 + - type: object 17 + properties: 18 + name: 19 + type: string 20 + # Test case 2: Array with allOf of primitives 21 + ArrayWithAllOfPrimitives: 22 + type: array 23 + items: 24 + allOf: 25 + - type: number 26 + - type: string 27 + # Test case 3: Array with allOf including refs 28 + ArrayWithAllOfRefs: 29 + type: array 30 + items: 31 + allOf: 32 + - $ref: '#/components/schemas/BaseModel' 33 + - type: object 34 + properties: 35 + extra: 36 + type: string 37 + BaseModel: 38 + type: object 39 + properties: 40 + id: 41 + type: integer 42 + createdAt: 43 + type: string 44 + format: date-time
+44
packages/openapi-ts-tests/specs/3.1.x/array-items-all-of.yaml
··· 1 + openapi: 3.1.0 2 + info: 3 + title: OpenAPI 3.1.0 array items allOf example 4 + version: '1' 5 + components: 6 + schemas: 7 + # Test case 1: Array with allOf of object schemas 8 + ArrayWithAllOfObjects: 9 + type: array 10 + items: 11 + allOf: 12 + - type: object 13 + properties: 14 + id: 15 + type: integer 16 + - type: object 17 + properties: 18 + name: 19 + type: string 20 + # Test case 2: Array with allOf of primitives 21 + ArrayWithAllOfPrimitives: 22 + type: array 23 + items: 24 + allOf: 25 + - type: number 26 + - type: string 27 + # Test case 3: Array with allOf including refs 28 + ArrayWithAllOfRefs: 29 + type: array 30 + items: 31 + allOf: 32 + - $ref: '#/components/schemas/BaseModel' 33 + - type: object 34 + properties: 35 + extra: 36 + type: string 37 + BaseModel: 38 + type: object 39 + properties: 40 + id: 41 + type: integer 42 + createdAt: 43 + type: string 44 + format: date-time
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/v4-mini'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ 6 + id: z.optional(z.int()) 7 + }), z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.number().int().optional() 7 + }).and(z.object({ 8 + name: z.string().optional() 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.number().int().optional(), 15 + createdAt: z.string().datetime().optional() 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.string().optional() 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v4'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.optional(z.int()) 7 + }).and(z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/v4-mini'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ 6 + id: z.optional(z.int()) 7 + }), z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.number().int().optional() 7 + }).and(z.object({ 8 + name: z.string().optional() 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.number().int().optional(), 15 + createdAt: z.string().datetime().optional() 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.string().optional() 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v4'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.optional(z.int()) 7 + }).and(z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/v4-mini'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ 6 + id: z.optional(z.int()) 7 + }), z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.number().int().optional() 7 + }).and(z.object({ 8 + name: z.string().optional() 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.number().int().optional(), 15 + createdAt: z.string().datetime().optional() 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.string().optional() 20 + })));
+20
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v4'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.optional(z.int()) 7 + }).and(z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+8
packages/openapi-ts-tests/zod/v3/test/openapi.test.ts
··· 37 37 const scenarios = [ 38 38 { 39 39 config: createConfig({ 40 + input: 'array-items-all-of.yaml', 41 + output: 'array-items-all-of', 42 + }), 43 + description: 44 + 'generates correct array when items use allOf (intersection)', 45 + }, 46 + { 47 + config: createConfig({ 40 48 input: 'full.yaml', 41 49 output: 'default', 42 50 }),
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/mini'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ 6 + id: z.optional(z.int()) 7 + }), z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v3'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.number().int().optional() 7 + }).and(z.object({ 8 + name: z.string().optional() 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.number().int().optional(), 15 + createdAt: z.string().datetime().optional() 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.string().optional() 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.optional(z.int()) 7 + }).and(z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/mini'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ 6 + id: z.optional(z.int()) 7 + }), z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v3'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.number().int().optional() 7 + }).and(z.object({ 8 + name: z.string().optional() 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.number().int().optional(), 15 + createdAt: z.string().datetime().optional() 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.string().optional() 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.optional(z.int()) 7 + }).and(z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/mini'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ 6 + id: z.optional(z.int()) 7 + }), z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v3'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.number().int().optional() 7 + }).and(z.object({ 8 + name: z.string().optional() 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.number().int().optional(), 15 + createdAt: z.string().datetime().optional() 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.string().optional() 20 + })));
+20
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zArrayWithAllOfObjects = z.array(z.object({ 6 + id: z.optional(z.int()) 7 + }).and(z.object({ 8 + name: z.optional(z.string()) 9 + }))); 10 + 11 + export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); 12 + 13 + export const zBaseModel = z.object({ 14 + id: z.optional(z.int()), 15 + createdAt: z.optional(z.iso.datetime()) 16 + }); 17 + 18 + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ 19 + extra: z.optional(z.string()) 20 + })));
+8
packages/openapi-ts-tests/zod/v4/test/openapi.test.ts
··· 37 37 const scenarios = [ 38 38 { 39 39 config: createConfig({ 40 + input: 'array-items-all-of.yaml', 41 + output: 'array-items-all-of', 42 + }), 43 + description: 44 + 'generates correct array when items use allOf (intersection)', 45 + }, 46 + { 47 + config: createConfig({ 40 48 input: 'full.yaml', 41 49 output: 'default', 42 50 }),
+49 -20
packages/openapi-ts/src/plugins/zod/mini/plugin.ts
··· 69 69 }); 70 70 } else { 71 71 if (schema.logicalOperator === 'and') { 72 - // TODO: parser - handle intersection 73 - // return tsc.typeArrayNode( 74 - // tsc.typeIntersectionNode({ types: itemExpressions }), 75 - // ); 76 - } 77 - 78 - result.expression = tsc.callExpression({ 79 - functionName: tsc.propertyAccessExpression({ 80 - expression: zSymbol.placeholder, 81 - name: identifiers.array, 82 - }), 83 - parameters: [ 84 - tsc.callExpression({ 72 + const firstSchema = schema.items![0]!; 73 + // we want to add an intersection, but not every schema can use the same API. 74 + // if the first item contains another array or not an object, we cannot use 75 + // `.intersection()` as that does not exist on `.union()` and non-object schemas. 76 + let intersectionExpression: ts.Expression; 77 + if ( 78 + firstSchema.logicalOperator === 'or' || 79 + (firstSchema.type && firstSchema.type !== 'object') 80 + ) { 81 + intersectionExpression = tsc.callExpression({ 85 82 functionName: tsc.propertyAccessExpression({ 86 83 expression: zSymbol.placeholder, 87 - name: identifiers.union, 84 + name: identifiers.intersection, 88 85 }), 89 - parameters: [ 90 - tsc.arrayLiteralExpression({ 91 - elements: itemExpressions, 86 + parameters: itemExpressions, 87 + }); 88 + } else { 89 + intersectionExpression = itemExpressions[0]!; 90 + for (let i = 1; i < itemExpressions.length; i++) { 91 + intersectionExpression = tsc.callExpression({ 92 + functionName: tsc.propertyAccessExpression({ 93 + expression: zSymbol.placeholder, 94 + name: identifiers.intersection, 92 95 }), 93 - ], 96 + parameters: [intersectionExpression, itemExpressions[i]!], 97 + }); 98 + } 99 + } 100 + 101 + result.expression = tsc.callExpression({ 102 + functionName, 103 + parameters: [intersectionExpression], 104 + }); 105 + } else { 106 + result.expression = tsc.callExpression({ 107 + functionName: tsc.propertyAccessExpression({ 108 + expression: zSymbol.placeholder, 109 + name: identifiers.array, 94 110 }), 95 - ], 96 - }); 111 + parameters: [ 112 + tsc.callExpression({ 113 + functionName: tsc.propertyAccessExpression({ 114 + expression: zSymbol.placeholder, 115 + name: identifiers.union, 116 + }), 117 + parameters: [ 118 + tsc.arrayLiteralExpression({ 119 + elements: itemExpressions, 120 + }), 121 + ], 122 + }), 123 + ], 124 + }); 125 + } 97 126 } 98 127 } 99 128
+49 -20
packages/openapi-ts/src/plugins/zod/v3/plugin.ts
··· 72 72 }); 73 73 } else { 74 74 if (schema.logicalOperator === 'and') { 75 - // TODO: parser - handle intersection 76 - // return tsc.typeArrayNode( 77 - // tsc.typeIntersectionNode({ types: itemExpressions }), 78 - // ); 79 - } 80 - 81 - arrayExpression = tsc.callExpression({ 82 - functionName: tsc.propertyAccessExpression({ 83 - expression: zSymbol.placeholder, 84 - name: identifiers.array, 85 - }), 86 - parameters: [ 87 - tsc.callExpression({ 75 + const firstSchema = schema.items![0]!; 76 + // we want to add an intersection, but not every schema can use the same API. 77 + // if the first item contains another array or not an object, we cannot use 78 + // `.and()` as that does not exist on `.union()` and non-object schemas. 79 + let intersectionExpression: ts.Expression; 80 + if ( 81 + firstSchema.logicalOperator === 'or' || 82 + (firstSchema.type && firstSchema.type !== 'object') 83 + ) { 84 + intersectionExpression = tsc.callExpression({ 88 85 functionName: tsc.propertyAccessExpression({ 89 86 expression: zSymbol.placeholder, 90 - name: identifiers.union, 87 + name: identifiers.intersection, 91 88 }), 92 - parameters: [ 93 - tsc.arrayLiteralExpression({ 94 - elements: itemExpressions, 89 + parameters: itemExpressions, 90 + }); 91 + } else { 92 + intersectionExpression = itemExpressions[0]!; 93 + for (let i = 1; i < itemExpressions.length; i++) { 94 + intersectionExpression = tsc.callExpression({ 95 + functionName: tsc.propertyAccessExpression({ 96 + expression: intersectionExpression, 97 + name: identifiers.and, 95 98 }), 96 - ], 99 + parameters: [itemExpressions[i]!], 100 + }); 101 + } 102 + } 103 + 104 + arrayExpression = tsc.callExpression({ 105 + functionName, 106 + parameters: [intersectionExpression], 107 + }); 108 + } else { 109 + arrayExpression = tsc.callExpression({ 110 + functionName: tsc.propertyAccessExpression({ 111 + expression: zSymbol.placeholder, 112 + name: identifiers.array, 97 113 }), 98 - ], 99 - }); 114 + parameters: [ 115 + tsc.callExpression({ 116 + functionName: tsc.propertyAccessExpression({ 117 + expression: zSymbol.placeholder, 118 + name: identifiers.union, 119 + }), 120 + parameters: [ 121 + tsc.arrayLiteralExpression({ 122 + elements: itemExpressions, 123 + }), 124 + ], 125 + }), 126 + ], 127 + }); 128 + } 100 129 } 101 130 } 102 131
+49 -20
packages/openapi-ts/src/plugins/zod/v4/plugin.ts
··· 69 69 }); 70 70 } else { 71 71 if (schema.logicalOperator === 'and') { 72 - // TODO: parser - handle intersection 73 - // return tsc.typeArrayNode( 74 - // tsc.typeIntersectionNode({ types: itemExpressions }), 75 - // ); 76 - } 77 - 78 - result.expression = tsc.callExpression({ 79 - functionName: tsc.propertyAccessExpression({ 80 - expression: zSymbol.placeholder, 81 - name: identifiers.array, 82 - }), 83 - parameters: [ 84 - tsc.callExpression({ 72 + const firstSchema = schema.items![0]!; 73 + // we want to add an intersection, but not every schema can use the same API. 74 + // if the first item contains another array or not an object, we cannot use 75 + // `.and()` as that does not exist on `.union()` and non-object schemas. 76 + let intersectionExpression: ts.Expression; 77 + if ( 78 + firstSchema.logicalOperator === 'or' || 79 + (firstSchema.type && firstSchema.type !== 'object') 80 + ) { 81 + intersectionExpression = tsc.callExpression({ 85 82 functionName: tsc.propertyAccessExpression({ 86 83 expression: zSymbol.placeholder, 87 - name: identifiers.union, 84 + name: identifiers.intersection, 88 85 }), 89 - parameters: [ 90 - tsc.arrayLiteralExpression({ 91 - elements: itemExpressions, 86 + parameters: itemExpressions, 87 + }); 88 + } else { 89 + intersectionExpression = itemExpressions[0]!; 90 + for (let i = 1; i < itemExpressions.length; i++) { 91 + intersectionExpression = tsc.callExpression({ 92 + functionName: tsc.propertyAccessExpression({ 93 + expression: intersectionExpression, 94 + name: identifiers.and, 92 95 }), 93 - ], 96 + parameters: [itemExpressions[i]!], 97 + }); 98 + } 99 + } 100 + 101 + result.expression = tsc.callExpression({ 102 + functionName, 103 + parameters: [intersectionExpression], 104 + }); 105 + } else { 106 + result.expression = tsc.callExpression({ 107 + functionName: tsc.propertyAccessExpression({ 108 + expression: zSymbol.placeholder, 109 + name: identifiers.array, 94 110 }), 95 - ], 96 - }); 111 + parameters: [ 112 + tsc.callExpression({ 113 + functionName: tsc.propertyAccessExpression({ 114 + expression: zSymbol.placeholder, 115 + name: identifiers.union, 116 + }), 117 + parameters: [ 118 + tsc.arrayLiteralExpression({ 119 + elements: itemExpressions, 120 + }), 121 + ], 122 + }), 123 + ], 124 + }); 125 + } 97 126 } 98 127 } 99 128