Skip to content

Commit aa79764

Browse files
authored
Ensure discriminator mappings use union enum literals (#1424)
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 894e74d commit aa79764

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)