From 6b6e9dd9bdc2fe95189c0e47274dc711824e3f7b Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Fri, 19 Sep 2025 07:10:45 +0900 Subject: [PATCH] Ensure discriminator mappings use union enum literals Resolve discriminator mapping generation to use literal values when generateUnionEnums is enabled to avoid emitting enum member references. Add regression coverage that snapshots the discriminator output with union enums. Signed-off-by: Sora Morimoto --- .changeset/salty-pets-notice.md | 10 + .../base-schema-parsers/discriminator.ts | 24 +- .../__snapshots__/basic.test.ts.snap | 229 ++++++++++++++++++ tests/spec/discriminator/basic.test.ts | 18 ++ 4 files changed, 271 insertions(+), 10 deletions(-) create mode 100644 .changeset/salty-pets-notice.md diff --git a/.changeset/salty-pets-notice.md b/.changeset/salty-pets-notice.md new file mode 100644 index 000000000..0b599e3f9 --- /dev/null +++ b/.changeset/salty-pets-notice.md @@ -0,0 +1,10 @@ +--- +"swagger-typescript-api": patch +--- + +Ensure discriminator mappings use union enum literals. + +Resolve discriminator mapping generation to use literal values when +`generateUnionEnums` is enabled to avoid emitting enum member references. +Add regression coverage that snapshots the discriminator output with +union enums. diff --git a/src/schema-parser/base-schema-parsers/discriminator.ts b/src/schema-parser/base-schema-parsers/discriminator.ts index 4fce9b174..c79e5b498 100644 --- a/src/schema-parser/base-schema-parsers/discriminator.ts +++ b/src/schema-parser/base-schema-parsers/discriminator.ts @@ -177,18 +177,22 @@ export class DiscriminatorSchemaParser extends MonoSchemaParser { ); } - if ( - mappingPropertySchema?.rawTypeData?.$parsed?.type === SCHEMA_TYPES.ENUM - ) { + const parsedEnum = mappingPropertySchema?.rawTypeData?.$parsed; + if (parsedEnum?.type === SCHEMA_TYPES.ENUM) { mappingPropertySchemaEnumKeysMap = lodash.reduce( - mappingPropertySchema.rawTypeData.$parsed.enum, + parsedEnum.enum, (acc, key, index) => { - const enumKey = - mappingPropertySchema.rawTypeData.$parsed.content[index].key; - acc[key] = ts.EnumUsageKey( - mappingPropertySchema.rawTypeData.$parsed.typeName, - enumKey, - ); + const enumContent = parsedEnum.content?.[index]; + if (this.config.generateUnionEnums) { + const literalValue = + enumContent?.value ?? + (key !== undefined ? ts.StringValue(key) : undefined); + if (literalValue !== undefined) { + acc[key] = literalValue; + } + } else if (parsedEnum.typeName && enumContent?.key) { + acc[key] = ts.EnumUsageKey(parsedEnum.typeName, enumContent.key); + } return acc; }, {}, diff --git a/tests/spec/discriminator/__snapshots__/basic.test.ts.snap b/tests/spec/discriminator/__snapshots__/basic.test.ts.snap index ceeccad59..def7b89e8 100644 --- a/tests/spec/discriminator/__snapshots__/basic.test.ts.snap +++ b/tests/spec/discriminator/__snapshots__/basic.test.ts.snap @@ -236,3 +236,232 @@ type BaseBlockDtoWithEnumTypeMapping = { } & Type; " `; + +exports[`basic > discriminator with union enums 1`] = ` +"/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export type PetEnum = "dog" | "lizard" | "cat"; + +export type BlockDTOEnum = "csv" | "file" | "kek"; + +/** kek pek */ +export type Variant = + | ({ + type: "update"; + } & VariantUpdate) + | ({ + type: "undo"; + } & VariantUndo) + | ({ + type: "rollback"; + } & VariantRollback) + | ({ + type: "scale"; + } & VariantScale) + | ({ + type: "resources"; + } & VariantResources) + | ({ + type: "firewall"; + } & VariantFirewall) + | ({ + type: "gateway"; + } & VariantGateway); + +export type InvalidDiscriminatorPropertyName = + BaseInvalidDiscriminatorPropertyName & + ( + | BaseInvalidDiscriminatorPropertyNameTypeMapping<"num", number> + | BaseInvalidDiscriminatorPropertyNameTypeMapping<"str", string> + ); + +export type PetWithEnum = BasePetWithEnum & + ( + | BasePetWithEnumPetTypeMapping<"dog", DogWithEnum> + | BasePetWithEnumPetTypeMapping<"cat", CatWithEnum> + | BasePetWithEnumPetTypeMapping<"lizard", LizardWithEnum> + ); + +export type PetOnlyDiscriminator = + | ({ + pet_type: "dog"; + } & Dog) + | ({ + pet_type: "cat"; + } & Cat) + | ({ + pet_type: "lizard"; + } & Lizard); + +export type Pet = BasePet & + ( + | BasePetPetTypeMapping<"dog", Dog> + | BasePetPetTypeMapping<"cat", Cat> + | BasePetPetTypeMapping<"lizard", Lizard> + ); + +export type BlockDTO = BaseBlockDto & + ( + | BaseBlockDtoTypeMapping<"csv", CsvBlockDTO> + | BaseBlockDtoTypeMapping<"file", FileBlockDTO> + ); + +export type BlockDTOWithEnum = BaseBlockDtoWithEnum & + ( + | BaseBlockDtoWithEnumTypeMapping<"csv", CsvBlockWithEnumDTO> + | BaseBlockDtoWithEnumTypeMapping<"file", FileBlockWithEnumDTO> + ); + +export type SimpleDiscriminator = SimpleObject | ComplexObject; + +export interface SimpleObject { + objectType: string; +} + +export interface ComplexObject { + objectType: string; +} + +export type CsvBlockWithEnumDTO = BaseBlockDtoWithEnum & { + type: "csv"; + text: string; +}; + +export type FileBlockWithEnumDTO = BaseBlockDtoWithEnum & { + type: "file"; + fileId: string; +}; + +export type CsvBlockDTO = BaseBlockDto & { + /** @default "csv" */ + type: "csv"; + text: string; +}; + +export type FileBlockDTO = BaseBlockDto & { + /** @default "file" */ + type: "file"; + fileId: string; +}; + +export type Cat = BasePet & { + name?: string; +}; + +export type Dog = BasePet & { + bark?: string; +}; + +export type Lizard = BasePet & { + lovesRocks?: boolean; +}; + +export type CatWithEnum = BasePetWithEnum & { + name?: string; +}; + +export type DogWithEnum = BasePetWithEnum & { + bark?: string; +}; + +export type LizardWithEnum = BasePetWithEnum & { + lovesRocks?: boolean; +}; + +/** Proposal to change firewall rules for deployment. */ +export interface VariantFirewall { + /** asdasdasdasdasdsad added to deployment. If not set, no rules are added. */ + rules_added?: string[]; + /** asdasdasdasdasdsad removed from deployment. If not set, no rules were removed. */ + rules_removed?: string[]; +} + +/** asdasdasdasdasd */ +export interface VariantScale { + /** + * asdasdasdasdasdsad + * @example 3 + */ + replicas: number; +} + +/** asdasdasdasdasd */ +export interface VariantResources { + resources: string; +} + +/** asdasdasdasdasd */ +export interface VariantGateway { + /** asdasdasdasdasdsad */ + port?: string; + /** asdasdasdasdasdsad */ + name?: string; + /** asdasdasdasdasdsad */ + domain?: string; +} + +/** Pasdasdasdasdasd. */ +export type VariantUpdate = object; + +/** asdasdasdasdasd */ +export interface VariantRollback { + /** + * asdasdasdasdasdsad + * @example 42 + */ + revision_id: number; +} + +/** asdasdasdasdasdn */ +export type VariantUndo = object; + +type BaseInvalidDiscriminatorPropertyName = object; + +type BaseInvalidDiscriminatorPropertyNameTypeMapping = { + "@type": Key; +} & Type; + +interface BasePetWithEnum { + pet_type: PetEnum; +} + +type BasePetWithEnumPetTypeMapping = { + pet_type: Key; +} & Type; + +interface BasePet { + pet_type: string; +} + +type BasePetPetTypeMapping = { + pet_type: Key; +} & Type; + +interface BaseBlockDto { + title: string; +} + +type BaseBlockDtoTypeMapping = { + type: Key; +} & Type; + +interface BaseBlockDtoWithEnum { + title: string; + type: BlockDTOEnum; +} + +type BaseBlockDtoWithEnumTypeMapping = { + type: Key; +} & Type; +" +`; diff --git a/tests/spec/discriminator/basic.test.ts b/tests/spec/discriminator/basic.test.ts index a1a248d60..d347e040b 100644 --- a/tests/spec/discriminator/basic.test.ts +++ b/tests/spec/discriminator/basic.test.ts @@ -31,4 +31,22 @@ describe("basic", async () => { expect(content).toMatchSnapshot(); }); + + test("discriminator with union enums", async () => { + await generateApi({ + fileName: "schema", + input: path.resolve(import.meta.dirname, "schema.json"), + output: tmpdir, + silent: true, + addReadonly: true, + generateClient: false, + generateUnionEnums: true, + }); + + const content = await fs.readFile(path.join(tmpdir, "schema.ts"), { + encoding: "utf8", + }); + + expect(content).toMatchSnapshot(); + }); });