From 6dfc1688ea1c5cf5e2335236bd7abe9476c8c4ab Mon Sep 17 00:00:00 2001 From: Benson Shen Date: Wed, 22 Oct 2025 16:36:02 -0400 Subject: [PATCH 1/2] chore: add test for non-object intersections Ticket: dx-2181 --- .../openapi-generator/test/optimize.test.ts | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/packages/openapi-generator/test/optimize.test.ts b/packages/openapi-generator/test/optimize.test.ts index d37b44ba..8bc84dad 100644 --- a/packages/openapi-generator/test/optimize.test.ts +++ b/packages/openapi-generator/test/optimize.test.ts @@ -180,3 +180,72 @@ test('non-consolidatable unions are not consolidated', () => { assert.deepEqual(optimize(input), expected); }); + +test('intersection with non-object types removes empty object', () => { + const input: Schema = { + type: 'intersection', + schemas: [ + { + type: 'object', + properties: {}, + required: [], + }, + { + type: 'ref', + name: 'SomeType', + location: '/path/to/file.ts', + }, + ], + }; + + // The empty object should be removed, leaving just the ref + const expected: Schema = { + type: 'ref', + name: 'SomeType', + location: '/path/to/file.ts', + }; + + assert.deepEqual(optimize(input), expected); +}); + +test('intersection with multiple non-object types removes empty object', () => { + const input: Schema = { + type: 'intersection', + schemas: [ + { + type: 'object', + properties: {}, + required: [], + }, + { + type: 'ref', + name: 'TypeA', + location: '/path/to/file.ts', + }, + { + type: 'ref', + name: 'TypeB', + location: '/path/to/file.ts', + }, + ], + }; + + // The empty object should be removed, leaving the intersection of refs + const expected: Schema = { + type: 'intersection', + schemas: [ + { + type: 'ref', + name: 'TypeA', + location: '/path/to/file.ts', + }, + { + type: 'ref', + name: 'TypeB', + location: '/path/to/file.ts', + }, + ], + }; + + assert.deepEqual(optimize(input), expected); +}); From 713ecab6e24dedec675df116e4d8e4362c2410be Mon Sep 17 00:00:00 2001 From: Benson Shen Date: Wed, 22 Oct 2025 17:59:01 -0400 Subject: [PATCH 2/2] fix: fix the problem with empty objects in intersections Ticket: DX-2181 --- packages/openapi-generator/src/optimize.ts | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/openapi-generator/src/optimize.ts b/packages/openapi-generator/src/optimize.ts index dc652c4c..181c8362 100644 --- a/packages/openapi-generator/src/optimize.ts +++ b/packages/openapi-generator/src/optimize.ts @@ -1,5 +1,5 @@ import { combineComments } from './comments'; -import { isPrimitive, type Primitive, type Schema } from './ir'; +import { isPrimitive, type CombinedType, type Primitive, type Schema } from './ir'; export type OptimizeFn = (schema: Schema) => Schema; @@ -29,6 +29,28 @@ export function foldIntersection(schema: Schema, optimize: OptimizeFn): Schema { } }); + // If the combined object is empty (no properties) and result is an intersection, + // we can simplify by removing the empty object + const hasProperties = Object.keys(combinedObject.properties).length > 0; + + if (!hasProperties && result !== combinedObject) { + // At this point, result must be an intersection since it was reassigned + const intersectionResult = result as unknown as CombinedType; + + // Remove the empty object from the intersection + const nonEmptySchemas = intersectionResult.schemas.filter( + (s: Schema) => !(s.type === 'object' && Object.keys(s.properties).length === 0), + ); + + if (nonEmptySchemas.length === 0) { + return combinedObject; + } else if (nonEmptySchemas.length === 1) { + return nonEmptySchemas[0]!; + } else { + return { type: 'intersection', schemas: nonEmptySchemas }; + } + } + return result; }