Skip to content

Commit 914e049

Browse files
authored
Fix complex oneOf (#1207)
* Fix complex OneOf schemas * Fall back to TS unions for long oneOf comparisons
1 parent de9792f commit 914e049

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

.changeset/tasty-knives-matter.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-typescript": patch
3+
---
4+
5+
Fall back to TypeScript unions for long oneOf lists

packages/openapi-typescript/src/utils.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,17 @@ export function tsNonNullable(type: string): string {
180180
return `NonNullable<${type}>`;
181181
}
182182

183-
/** OneOf<T> (custom) */
183+
/**
184+
* OneOf<T>
185+
* TypeScript unions are not exclusive @see https://stackoverflow.com/questions/42123407/does-typescript-support-mutually-exclusive-types
186+
* However, at a certain size, the helper type becomes too complex for inference to work. Hence the > check.
187+
*/
184188
export function tsOneOf(...types: string[]): string {
185-
if (types.length === 1) return types[0];
189+
if (types.length === 1) {
190+
return types[0];
191+
} else if (types.length > 5) {
192+
return tsUnionOf(...types);
193+
}
186194
return `OneOf<[${types.join(", ")}]>`;
187195
}
188196

packages/openapi-typescript/test/schema-object.test.ts

+30
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,36 @@ describe("Schema Object", () => {
335335
}]>`);
336336
});
337337

338+
test("falls back to union at high complexity", () => {
339+
const schema: SchemaObject = {
340+
oneOf: [
341+
{ type: "object", properties: { string: { type: "string" } }, required: ["string"] },
342+
{ type: "object", properties: { boolean: { type: "boolean" } }, required: ["boolean"] },
343+
{ type: "object", properties: { number: { type: "number" } }, required: ["number"] },
344+
{ type: "object", properties: { array: { type: "array", items: { type: "string" } } }, required: ["array"] },
345+
{ type: "object", properties: { object: { type: "object", properties: { string: { type: "string" } }, required: ["string"] } }, required: ["object"] },
346+
{ type: "object", properties: { enum: { type: "string", enum: ["foo", "bar", "baz"] } }, required: ["enum"] },
347+
],
348+
};
349+
const generated = transformSchemaObject(schema, options);
350+
expect(generated).toBe(`{
351+
string: string;
352+
} | {
353+
boolean: boolean;
354+
} | {
355+
number: number;
356+
} | {
357+
array: (string)[];
358+
} | {
359+
object: {
360+
string: string;
361+
};
362+
} | ({
363+
/** @enum {string} */
364+
enum: "foo" | "bar" | "baz";
365+
})`);
366+
});
367+
338368
test("discriminator", () => {
339369
const schema: SchemaObject = {
340370
type: "object",

0 commit comments

Comments
 (0)