diff --git a/.changeset/real-penguins-fold.md b/.changeset/real-penguins-fold.md new file mode 100644 index 000000000..15a9fcf76 --- /dev/null +++ b/.changeset/real-penguins-fold.md @@ -0,0 +1,5 @@ +--- +"openapi-typescript": patch +--- + +Support for generating enums when enums definition has null value diff --git a/packages/openapi-typescript/src/transform/schema-object.ts b/packages/openapi-typescript/src/transform/schema-object.ts index 4ad571523..803a29d9e 100644 --- a/packages/openapi-typescript/src/transform/schema-object.ts +++ b/packages/openapi-typescript/src/transform/schema-object.ts @@ -94,7 +94,10 @@ export function transformSchemaObjectWithComposition( !("additionalProperties" in schemaObject) ) { // hoist enum to top level if string/number enum and option is enabled - if (options.ctx.enum && schemaObject.enum.every((v) => typeof v === "string" || typeof v === "number")) { + if ( + options.ctx.enum && + schemaObject.enum.every((v) => typeof v === "string" || typeof v === "number" || v === null) + ) { let enumName = parseRef(options.path ?? "").pointer.join("/"); // allow #/components/schemas to have simpler names enumName = enumName.replace("components/schemas", ""); @@ -102,7 +105,18 @@ export function transformSchemaObjectWithComposition( name: schemaObject["x-enum-varnames"]?.[i] ?? schemaObject["x-enumNames"]?.[i], description: schemaObject["x-enum-descriptions"]?.[i] ?? schemaObject["x-enumDescriptions"]?.[i], })); - const enumType = tsEnum(enumName, schemaObject.enum as (string | number)[], metadata, { + + // enums can contain null values, but dont want to output them + let hasNull = false; + const validSchemaEnums = schemaObject.enum.filter((enumValue) => { + if (enumValue === null) { + hasNull = true; + return false; + } + + return true; + }); + const enumType = tsEnum(enumName, validSchemaEnums as (string | number)[], metadata, { shouldCache: options.ctx.dedupeEnums, export: true, // readonly: TS enum do not support the readonly modifier @@ -110,7 +124,8 @@ export function transformSchemaObjectWithComposition( if (!options.ctx.injectFooter.includes(enumType)) { options.ctx.injectFooter.push(enumType); } - return ts.factory.createTypeReferenceNode(enumType.name); + const ref = ts.factory.createTypeReferenceNode(enumType.name); + return hasNull ? tsUnion([ref, NULL]) : ref; } const enumType = schemaObject.enum.map(tsLiteral); if ( diff --git a/packages/openapi-typescript/test/transform/schema-object/string.test.ts b/packages/openapi-typescript/test/transform/schema-object/string.test.ts index e8acac4a6..e5e9a5ac1 100644 --- a/packages/openapi-typescript/test/transform/schema-object/string.test.ts +++ b/packages/openapi-typescript/test/transform/schema-object/string.test.ts @@ -84,6 +84,13 @@ describe("transformSchemaObject > string", () => { want: '"A" | "B" | "C" | null', }, ], + [ + "enum + nullable + null value", + { + given: { type: ["string", "null"], enum: ["A", "B", "C", null] }, + want: '"A" | "B" | "C" | null', + }, + ], [ "enum + nullable (deprecated syntax)", {