Skip to content

Commit 270aad6

Browse files
committed
feat(enum-values): support record type enums
1 parent ed4dcfb commit 270aad6

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ import type { ReferenceObject, SchemaObject, TransformNodeOptions } from "../typ
3434
export default function transformSchemaObject(
3535
schemaObject: SchemaObject | ReferenceObject,
3636
options: TransformNodeOptions,
37+
fromAdditionalProperties = false,
3738
): ts.TypeNode {
38-
const type = transformSchemaObjectWithComposition(schemaObject, options);
39+
const type = transformSchemaObjectWithComposition(schemaObject, options, fromAdditionalProperties);
3940
if (typeof options.ctx.postTransform === "function") {
4041
const postTransformResult = options.ctx.postTransform(type, options);
4142
if (postTransformResult) {
@@ -51,6 +52,7 @@ export default function transformSchemaObject(
5152
export function transformSchemaObjectWithComposition(
5253
schemaObject: SchemaObject | ReferenceObject,
5354
options: TransformNodeOptions,
55+
fromAdditionalProperties = false,
5456
): ts.TypeNode {
5557
/**
5658
* Unexpected types & edge cases
@@ -145,7 +147,13 @@ export function transformSchemaObjectWithComposition(
145147

146148
const enumValuesArray = tsArrayLiteralExpression(
147149
enumValuesVariableName,
148-
oapiRef(options.path ?? "", undefined, true),
150+
// If fromAdditionalProperties is true we are dealing with a record type and we should append [string] to the generated type
151+
fromAdditionalProperties
152+
? ts.factory.createIndexedAccessTypeNode(
153+
oapiRef(options.path ?? "", undefined, true),
154+
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("string")),
155+
)
156+
: oapiRef(options.path ?? "", undefined, true),
149157
schemaObject.enum as (string | number)[],
150158
{
151159
export: true,
@@ -547,7 +555,7 @@ function transformSchemaObjectCore(schemaObject: SchemaObject, options: Transfor
547555
const hasExplicitAdditionalProperties =
548556
typeof schemaObject.additionalProperties === "object" && Object.keys(schemaObject.additionalProperties).length;
549557
const addlType = hasExplicitAdditionalProperties
550-
? transformSchemaObject(schemaObject.additionalProperties as SchemaObject, options)
558+
? transformSchemaObject(schemaObject.additionalProperties as SchemaObject, options, true)
551559
: UNKNOWN;
552560
return tsIntersection([
553561
...(coreObjectType.length ? [ts.factory.createTypeLiteralNode(coreObjectType)] : []),

packages/openapi-typescript/test/node-api.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,59 @@ type ReadonlyArray<T> = [
841841
export const pathsUrlGetParametersQueryStatusValues: ReadonlyArray<FlattenedDeepRequired<paths>["/url"]["get"]["parameters"]["query"]["status"]> = ["active", "inactive"];
842842
export const statusValues: ReadonlyArray<FlattenedDeepRequired<components>["schemas"]["Status"]> = ["active", "inactive"];
843843
export const errorCodeValues: ReadonlyArray<FlattenedDeepRequired<components>["schemas"]["ErrorCode"]> = [100, 101, 102, 103, 104, 105];
844+
export type operations = Record<string, never>;`,
845+
options: { enumValues: true },
846+
},
847+
],
848+
[
849+
"options > enumValues with record types",
850+
{
851+
given: {
852+
openapi: "3.1",
853+
info: { title: "Test", version: "1.0" },
854+
components: {
855+
schemas: {
856+
ComplexEditKeyDto: {
857+
type: "object",
858+
properties: {
859+
states: {
860+
type: "object",
861+
additionalProperties: {
862+
type: "string",
863+
enum: ["TRANSLATED", "REVIEWED"],
864+
},
865+
},
866+
},
867+
},
868+
},
869+
},
870+
},
871+
want: `export type paths = Record<string, never>;
872+
export type webhooks = Record<string, never>;
873+
export interface components {
874+
schemas: {
875+
ComplexEditKeyDto: {
876+
states?: {
877+
[key: string]: "TRANSLATED" | "REVIEWED";
878+
};
879+
};
880+
};
881+
responses: never;
882+
parameters: never;
883+
requestBodies: never;
884+
headers: never;
885+
pathItems: never;
886+
}
887+
export type $defs = Record<string, never>;
888+
type FlattenedDeepRequired<T> = {
889+
[K in keyof T]-?: FlattenedDeepRequired<T[K] extends unknown[] | undefined | null ? Extract<T[K], unknown[]>[number] : T[K]>;
890+
};
891+
type ReadonlyArray<T> = [
892+
Exclude<T, undefined>
893+
] extends [
894+
unknown[]
895+
] ? Readonly<Exclude<T, undefined>> : Readonly<Exclude<T, undefined>[]>;
896+
export const complexEditKeyDtoStatesValues: ReadonlyArray<FlattenedDeepRequired<components>["schemas"]["ComplexEditKeyDto"]["states"][string]> = ["TRANSLATED", "REVIEWED"];
844897
export type operations = Record<string, never>;`,
845898
options: { enumValues: true },
846899
},

0 commit comments

Comments
 (0)