Repository: samchungy/zod-openapi
Pull request #360 (2024-11-10 04:30:33)

Update Intersection Parser
https://github.com/rhinobase/hono-openapi/issues/3#issuecomment-2466572269


Patch:
diff --git a/src/create/schema/parsers/intersection.ts b/src/create/schema/parsers/intersection.ts
index 0012b79..5ee3ba6 100644
--- a/src/create/schema/parsers/intersection.ts
+++ b/src/create/schema/parsers/intersection.ts
@@ -1,5 +1,6 @@
 import type { ZodIntersection, ZodTypeAny } from 'zod';
 
+import { isZodType } from '../../../zodType';
 import {
   type Schema,
   type SchemaState,
@@ -15,18 +16,26 @@ export const createIntersectionSchema = <
   zodIntersection: ZodIntersection<T, U>,
   state: SchemaState,
 ): Schema => {
-  const left = createSchemaObject(zodIntersection._def.left, state, [
-    'intersection left',
-  ]);
-  const right = createSchemaObject(zodIntersection._def.right, state, [
-    'intersection right',
-  ]);
-
+  const schemas = flattenIntersection(zodIntersection);
+  const allOfs = schemas.map((schema, index) =>
+    createSchemaObject(schema, state, [`intersection ${index}`]),
+  );
   return {
     type: 'schema',
     schema: {
-      allOf: [left.schema, right.schema],
+      allOf: allOfs.map((schema) => schema.schema),
     },
-    effects: flattenEffects([left.effects, right.effects]),
+    effects: flattenEffects(allOfs.map((schema) => schema.effects)),
   };
 };
+
+export const flattenIntersection = (zodType: ZodTypeAny): ZodTypeAny[] => {
+  if (!isZodType(zodType, 'ZodIntersection')) {
+    return [zodType];
+  }
+
+  const leftSchemas = flattenIntersection(zodType._def.left);
+  const rightSchemas = flattenIntersection(zodType._def.right);
+
+  return [...leftSchemas, ...rightSchemas];
+};


Test patch:
diff --git a/src/create/schema/parsers/intersection.test.ts b/src/create/schema/parsers/intersection.test.ts
index 5a7451b..053f551 100644
--- a/src/create/schema/parsers/intersection.test.ts
+++ b/src/create/schema/parsers/intersection.test.ts
@@ -27,4 +27,98 @@ describe('createIntersectionSchema', () => {
 
     expect(result).toEqual(expected);
   });
+
+  it('creates an object with an allOf', () => {
+    const schema = z.object({
+      a: z.string(),
+    });
+
+    const andSchema = schema.and(
+      z.object({
+        b: z.string(),
+      }),
+    );
+
+    const result = createIntersectionSchema(andSchema, createOutputState());
+
+    expect(result).toEqual<Schema>({
+      type: 'schema',
+      schema: {
+        allOf: [
+          {
+            type: 'object',
+            properties: {
+              a: {
+                type: 'string',
+              },
+            },
+            required: ['a'],
+          },
+          {
+            type: 'object',
+            properties: {
+              b: {
+                type: 'string',
+              },
+            },
+            required: ['b'],
+          },
+        ],
+      },
+    });
+  });
+
+  it('attempts to flatten nested and usage', () => {
+    const schema = z.object({
+      a: z.string(),
+    });
+
+    const schema2 = z.object({
+      b: z.string(),
+    });
+
+    const schema3 = z.object({
+      c: z.string(),
+    });
+
+    const result = createIntersectionSchema(
+      schema.and(schema2).and(schema3),
+      createOutputState(),
+    );
+
+    expect(result).toEqual<Schema>({
+      type: 'schema',
+      schema: {
+        allOf: [
+          {
+            type: 'object',
+            properties: {
+              a: {
+                type: 'string',
+              },
+            },
+            required: ['a'],
+          },
+          {
+            type: 'object',
+            properties: {
+              b: {
+                type: 'string',
+              },
+            },
+            required: ['b'],
+          },
+          {
+            type: 'object',
+            properties: {
+              c: {
+                type: 'string',
+              },
+            },
+            required: ['c'],
+          },
+        ],
+      },
+    });
+  });
 });