Skip to content

Commit e51eee7

Browse files
authored
Merge pull request #1 from danitt/feat/conditional-enums
support conditional ts enums
2 parents 0a2793a + ad43b6a commit e51eee7

File tree

8 files changed

+382
-4
lines changed

8 files changed

+382
-4
lines changed

.changeset/every-roses-tickle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-typescript": minor
3+
---
4+
5+
Conditionally generate TS enums

docs/cli.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ The following flags are supported in the CLI:
113113
| `--empty-objects-unknown` | | `false` | Allow arbitrary properties for schema objects with no specified properties, and no specified `additionalProperties` |
114114
| `--enum` | | `false` | Generate true [TS enums](https://www.typescriptlang.org/docs/handbook/enums.html) rather than string unions. |
115115
| `--enum-values` | | `false` | Export enum values as arrays. |
116+
| `--conditional-enums` | | `false` | Only generate true TS enums when the `x-enum-*` metadata is available. Requires `--enum=true` to be enabled. |
116117
| `--dedupe-enums` | | `false` | Dedupe enum types when `--enum=true` is set |
117118
| `--check` | | `false` | Check that the generated types are up-to-date. |
118119
| `--exclude-deprecated` | | `false` | Exclude deprecated fields from types |

packages/openapi-typescript/bin/cli.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Options
1717
--output, -o Specify output file (if not specified in redocly.yaml)
1818
--enum Export true TS enums instead of unions
1919
--enum-values Export enum values as arrays
20+
--conditional-enums Only generate true TS enums when enum metadata is available (default: false)
2021
--dedupe-enums Dedupe enum types when \`--enum=true\` is set
2122
--check Check that the generated types are up-to-date. (default: false)
2223
--export-type, -t Export top-level \`type\` instead of \`interface\`
@@ -74,6 +75,7 @@ const flags = parser(args, {
7475
"emptyObjectsUnknown",
7576
"enum",
7677
"enumValues",
78+
"conditionalEnums",
7779
"dedupeEnums",
7880
"check",
7981
"excludeDeprecated",
@@ -139,6 +141,7 @@ async function generateSchema(schema, { redocly, silent = false }) {
139141
emptyObjectsUnknown: flags.emptyObjectsUnknown,
140142
enum: flags.enum,
141143
enumValues: flags.enumValues,
144+
conditionalEnums: flags.conditionalEnums,
142145
dedupeEnums: flags.dedupeEnums,
143146
excludeDeprecated: flags.excludeDeprecated,
144147
exportType: flags.exportType,

packages/openapi-typescript/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export default async function openapiTS(
7575
emptyObjectsUnknown: options.emptyObjectsUnknown ?? false,
7676
enum: options.enum ?? false,
7777
enumValues: options.enumValues ?? false,
78+
conditionalEnums: options.conditionalEnums ?? false,
7879
dedupeEnums: options.dedupeEnums ?? false,
7980
excludeDeprecated: options.excludeDeprecated ?? false,
8081
exportType: options.exportType ?? false,

packages/openapi-typescript/src/transform/schema-object.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,7 @@ export function transformSchemaObjectWithComposition(
9696
!("additionalProperties" in schemaObject)
9797
) {
9898
// hoist enum to top level if string/number enum and option is enabled
99-
if (
100-
options.ctx.enum &&
101-
schemaObject.enum.every((v) => typeof v === "string" || typeof v === "number" || v === null)
102-
) {
99+
if (shouldTransformToTsEnum(options, schemaObject)) {
103100
let enumName = parseRef(options.path ?? "").pointer.join("/");
104101
// allow #/components/schemas to have simpler names
105102
enumName = enumName.replace("components/schemas", "");
@@ -270,6 +267,35 @@ export function transformSchemaObjectWithComposition(
270267
return finalType;
271268
}
272269

270+
/**
271+
* Check if the given OAPI enum should be transformed to a TypeScript enum
272+
*/
273+
function shouldTransformToTsEnum(options: TransformNodeOptions, schemaObject: SchemaObject): boolean {
274+
// Enum conversion not enabled or no enum present
275+
if (!options.ctx.enum || !schemaObject.enum) {
276+
return false;
277+
}
278+
279+
// Enum must have string, number or null values
280+
if (!schemaObject.enum.every((v) => ["string", "number", null].includes(typeof v))) {
281+
return false;
282+
}
283+
284+
// If conditionalEnums is enabled, only convert if x-enum-* metadata is present
285+
if (options.ctx.conditionalEnums) {
286+
const hasEnumMetadata =
287+
Array.isArray(schemaObject["x-enum-varnames"]) ||
288+
Array.isArray(schemaObject["x-enumNames"]) ||
289+
Array.isArray(schemaObject["x-enum-descriptions"]) ||
290+
Array.isArray(schemaObject["x-enumDescriptions"]);
291+
if (!hasEnumMetadata) {
292+
return false;
293+
}
294+
}
295+
296+
return true;
297+
}
298+
273299
/**
274300
* Handle SchemaObject minus composition (anyOf/allOf/oneOf)
275301
*/

packages/openapi-typescript/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,8 @@ export interface OpenAPITSOptions {
651651
enum?: boolean;
652652
/** Export union values as arrays */
653653
enumValues?: boolean;
654+
/** Only generate TS Enums when `x-enum-*` metadata is available */
655+
conditionalEnums?: boolean;
654656
/** Dedupe enum values */
655657
dedupeEnums?: boolean;
656658
/** (optional) Substitute path parameter names with their respective types */
@@ -688,6 +690,7 @@ export interface GlobalContext {
688690
emptyObjectsUnknown: boolean;
689691
enum: boolean;
690692
enumValues: boolean;
693+
conditionalEnums: boolean;
691694
dedupeEnums: boolean;
692695
excludeDeprecated: boolean;
693696
exportType: boolean;

packages/openapi-typescript/test/test-helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const DEFAULT_CTX: GlobalContext = {
1515
emptyObjectsUnknown: false,
1616
enum: false,
1717
enumValues: false,
18+
conditionalEnums: false,
1819
dedupeEnums: false,
1920
excludeDeprecated: false,
2021
exportType: false,

0 commit comments

Comments
 (0)