Skip to content

Commit 6b6e9dd

Browse files
committed
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 <sora@morimoto.io>
1 parent 724b0cd commit 6b6e9dd

File tree

4 files changed

+271
-10
lines changed

4 files changed

+271
-10
lines changed

.changeset/salty-pets-notice.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"swagger-typescript-api": patch
3+
---
4+
5+
Ensure discriminator mappings use union enum literals.
6+
7+
Resolve discriminator mapping generation to use literal values when
8+
`generateUnionEnums` is enabled to avoid emitting enum member references.
9+
Add regression coverage that snapshots the discriminator output with
10+
union enums.

src/schema-parser/base-schema-parsers/discriminator.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,22 @@ export class DiscriminatorSchemaParser extends MonoSchemaParser {
177177
);
178178
}
179179

180-
if (
181-
mappingPropertySchema?.rawTypeData?.$parsed?.type === SCHEMA_TYPES.ENUM
182-
) {
180+
const parsedEnum = mappingPropertySchema?.rawTypeData?.$parsed;
181+
if (parsedEnum?.type === SCHEMA_TYPES.ENUM) {
183182
mappingPropertySchemaEnumKeysMap = lodash.reduce(
184-
mappingPropertySchema.rawTypeData.$parsed.enum,
183+
parsedEnum.enum,
185184
(acc, key, index) => {
186-
const enumKey =
187-
mappingPropertySchema.rawTypeData.$parsed.content[index].key;
188-
acc[key] = ts.EnumUsageKey(
189-
mappingPropertySchema.rawTypeData.$parsed.typeName,
190-
enumKey,
191-
);
185+
const enumContent = parsedEnum.content?.[index];
186+
if (this.config.generateUnionEnums) {
187+
const literalValue =
188+
enumContent?.value ??
189+
(key !== undefined ? ts.StringValue(key) : undefined);
190+
if (literalValue !== undefined) {
191+
acc[key] = literalValue;
192+
}
193+
} else if (parsedEnum.typeName && enumContent?.key) {
194+
acc[key] = ts.EnumUsageKey(parsedEnum.typeName, enumContent.key);
195+
}
192196
return acc;
193197
},
194198
{},

tests/spec/discriminator/__snapshots__/basic.test.ts.snap

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,232 @@ type BaseBlockDtoWithEnumTypeMapping<Key, Type> = {
236236
} & Type;
237237
"
238238
`;
239+
240+
exports[`basic > discriminator with union enums 1`] = `
241+
"/* eslint-disable */
242+
/* tslint:disable */
243+
// @ts-nocheck
244+
/*
245+
* ---------------------------------------------------------------
246+
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
247+
* ## ##
248+
* ## AUTHOR: acacode ##
249+
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
250+
* ---------------------------------------------------------------
251+
*/
252+
253+
export type PetEnum = "dog" | "lizard" | "cat";
254+
255+
export type BlockDTOEnum = "csv" | "file" | "kek";
256+
257+
/** kek pek */
258+
export type Variant =
259+
| ({
260+
type: "update";
261+
} & VariantUpdate)
262+
| ({
263+
type: "undo";
264+
} & VariantUndo)
265+
| ({
266+
type: "rollback";
267+
} & VariantRollback)
268+
| ({
269+
type: "scale";
270+
} & VariantScale)
271+
| ({
272+
type: "resources";
273+
} & VariantResources)
274+
| ({
275+
type: "firewall";
276+
} & VariantFirewall)
277+
| ({
278+
type: "gateway";
279+
} & VariantGateway);
280+
281+
export type InvalidDiscriminatorPropertyName =
282+
BaseInvalidDiscriminatorPropertyName &
283+
(
284+
| BaseInvalidDiscriminatorPropertyNameTypeMapping<"num", number>
285+
| BaseInvalidDiscriminatorPropertyNameTypeMapping<"str", string>
286+
);
287+
288+
export type PetWithEnum = BasePetWithEnum &
289+
(
290+
| BasePetWithEnumPetTypeMapping<"dog", DogWithEnum>
291+
| BasePetWithEnumPetTypeMapping<"cat", CatWithEnum>
292+
| BasePetWithEnumPetTypeMapping<"lizard", LizardWithEnum>
293+
);
294+
295+
export type PetOnlyDiscriminator =
296+
| ({
297+
pet_type: "dog";
298+
} & Dog)
299+
| ({
300+
pet_type: "cat";
301+
} & Cat)
302+
| ({
303+
pet_type: "lizard";
304+
} & Lizard);
305+
306+
export type Pet = BasePet &
307+
(
308+
| BasePetPetTypeMapping<"dog", Dog>
309+
| BasePetPetTypeMapping<"cat", Cat>
310+
| BasePetPetTypeMapping<"lizard", Lizard>
311+
);
312+
313+
export type BlockDTO = BaseBlockDto &
314+
(
315+
| BaseBlockDtoTypeMapping<"csv", CsvBlockDTO>
316+
| BaseBlockDtoTypeMapping<"file", FileBlockDTO>
317+
);
318+
319+
export type BlockDTOWithEnum = BaseBlockDtoWithEnum &
320+
(
321+
| BaseBlockDtoWithEnumTypeMapping<"csv", CsvBlockWithEnumDTO>
322+
| BaseBlockDtoWithEnumTypeMapping<"file", FileBlockWithEnumDTO>
323+
);
324+
325+
export type SimpleDiscriminator = SimpleObject | ComplexObject;
326+
327+
export interface SimpleObject {
328+
objectType: string;
329+
}
330+
331+
export interface ComplexObject {
332+
objectType: string;
333+
}
334+
335+
export type CsvBlockWithEnumDTO = BaseBlockDtoWithEnum & {
336+
type: "csv";
337+
text: string;
338+
};
339+
340+
export type FileBlockWithEnumDTO = BaseBlockDtoWithEnum & {
341+
type: "file";
342+
fileId: string;
343+
};
344+
345+
export type CsvBlockDTO = BaseBlockDto & {
346+
/** @default "csv" */
347+
type: "csv";
348+
text: string;
349+
};
350+
351+
export type FileBlockDTO = BaseBlockDto & {
352+
/** @default "file" */
353+
type: "file";
354+
fileId: string;
355+
};
356+
357+
export type Cat = BasePet & {
358+
name?: string;
359+
};
360+
361+
export type Dog = BasePet & {
362+
bark?: string;
363+
};
364+
365+
export type Lizard = BasePet & {
366+
lovesRocks?: boolean;
367+
};
368+
369+
export type CatWithEnum = BasePetWithEnum & {
370+
name?: string;
371+
};
372+
373+
export type DogWithEnum = BasePetWithEnum & {
374+
bark?: string;
375+
};
376+
377+
export type LizardWithEnum = BasePetWithEnum & {
378+
lovesRocks?: boolean;
379+
};
380+
381+
/** Proposal to change firewall rules for deployment. */
382+
export interface VariantFirewall {
383+
/** asdasdasdasdasdsad added to deployment. If not set, no rules are added. */
384+
rules_added?: string[];
385+
/** asdasdasdasdasdsad removed from deployment. If not set, no rules were removed. */
386+
rules_removed?: string[];
387+
}
388+
389+
/** asdasdasdasdasd */
390+
export interface VariantScale {
391+
/**
392+
* asdasdasdasdasdsad
393+
* @example 3
394+
*/
395+
replicas: number;
396+
}
397+
398+
/** asdasdasdasdasd */
399+
export interface VariantResources {
400+
resources: string;
401+
}
402+
403+
/** asdasdasdasdasd */
404+
export interface VariantGateway {
405+
/** asdasdasdasdasdsad */
406+
port?: string;
407+
/** asdasdasdasdasdsad */
408+
name?: string;
409+
/** asdasdasdasdasdsad */
410+
domain?: string;
411+
}
412+
413+
/** Pasdasdasdasdasd. */
414+
export type VariantUpdate = object;
415+
416+
/** asdasdasdasdasd */
417+
export interface VariantRollback {
418+
/**
419+
* asdasdasdasdasdsad
420+
* @example 42
421+
*/
422+
revision_id: number;
423+
}
424+
425+
/** asdasdasdasdasdn */
426+
export type VariantUndo = object;
427+
428+
type BaseInvalidDiscriminatorPropertyName = object;
429+
430+
type BaseInvalidDiscriminatorPropertyNameTypeMapping<Key, Type> = {
431+
"@type": Key;
432+
} & Type;
433+
434+
interface BasePetWithEnum {
435+
pet_type: PetEnum;
436+
}
437+
438+
type BasePetWithEnumPetTypeMapping<Key, Type> = {
439+
pet_type: Key;
440+
} & Type;
441+
442+
interface BasePet {
443+
pet_type: string;
444+
}
445+
446+
type BasePetPetTypeMapping<Key, Type> = {
447+
pet_type: Key;
448+
} & Type;
449+
450+
interface BaseBlockDto {
451+
title: string;
452+
}
453+
454+
type BaseBlockDtoTypeMapping<Key, Type> = {
455+
type: Key;
456+
} & Type;
457+
458+
interface BaseBlockDtoWithEnum {
459+
title: string;
460+
type: BlockDTOEnum;
461+
}
462+
463+
type BaseBlockDtoWithEnumTypeMapping<Key, Type> = {
464+
type: Key;
465+
} & Type;
466+
"
467+
`;

tests/spec/discriminator/basic.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,22 @@ describe("basic", async () => {
3131

3232
expect(content).toMatchSnapshot();
3333
});
34+
35+
test("discriminator with union enums", async () => {
36+
await generateApi({
37+
fileName: "schema",
38+
input: path.resolve(import.meta.dirname, "schema.json"),
39+
output: tmpdir,
40+
silent: true,
41+
addReadonly: true,
42+
generateClient: false,
43+
generateUnionEnums: true,
44+
});
45+
46+
const content = await fs.readFile(path.join(tmpdir, "schema.ts"), {
47+
encoding: "utf8",
48+
});
49+
50+
expect(content).toMatchSnapshot();
51+
});
3452
});

0 commit comments

Comments
 (0)