diff --git a/.changeset/proud-students-repeat.md b/.changeset/proud-students-repeat.md new file mode 100644 index 000000000..d975a340e --- /dev/null +++ b/.changeset/proud-students-repeat.md @@ -0,0 +1,5 @@ +--- +"openapi-typescript": patch +--- + +fix(openapi-typescript): type errors generated under `enumValues` diff --git a/packages/openapi-typescript/src/lib/ts.ts b/packages/openapi-typescript/src/lib/ts.ts index c090a2eac..559d62ed3 100644 --- a/packages/openapi-typescript/src/lib/ts.ts +++ b/packages/openapi-typescript/src/lib/ts.ts @@ -242,13 +242,13 @@ export function tsArrayLiteralExpression( name: string, elementType: ts.TypeNode, values: (string | number)[], - options?: { export?: boolean; readonly?: boolean }, + options?: { export?: boolean; readonly?: boolean; injectFooter?: ts.Node[] }, ) { let variableName = sanitizeMemberName(name); variableName = `${variableName[0].toLowerCase()}${variableName.substring(1)}`; const arrayType = options?.readonly - ? ts.factory.createTypeReferenceNode("ReadonlyArray", [elementType]) + ? tsReadonlyArray(elementType, options.injectFooter) : ts.factory.createArrayTypeNode(elementType); return ts.factory.createVariableStatement( @@ -490,3 +490,21 @@ export function tsWithRequired( tsUnion(keys.map((k) => tsLiteral(k))), ]); } + +/** + * Enhanced ReadonlyArray. + * eg: type Foo = ReadonlyArray; type Bar = ReadonlyArray + * Foo and Bar are both of type `readonly T[]` + */ +export function tsReadonlyArray(type: ts.TypeNode, injectFooter?: ts.Node[]): ts.TypeNode { + if ( + injectFooter && + !injectFooter.some((node) => ts.isTypeAliasDeclaration(node) && node?.name?.escapedText === "ReadonlyArray") + ) { + const helper = stringToAST( + "type ReadonlyArray = [Exclude] extends [any[]] ? Readonly> : Readonly[]>;", + )[0] as any; + injectFooter.push(helper); + } + return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("ReadonlyArray"), [type]); +} diff --git a/packages/openapi-typescript/src/transform/schema-object.ts b/packages/openapi-typescript/src/transform/schema-object.ts index 4ad571523..7e74f094c 100644 --- a/packages/openapi-typescript/src/transform/schema-object.ts +++ b/packages/openapi-typescript/src/transform/schema-object.ts @@ -136,6 +136,7 @@ export function transformSchemaObjectWithComposition( { export: true, readonly: true, + injectFooter: options.ctx.injectFooter, }, ); diff --git a/packages/openapi-typescript/test/node-api.test.ts b/packages/openapi-typescript/test/node-api.test.ts index b0ef5f800..e476552a6 100644 --- a/packages/openapi-typescript/test/node-api.test.ts +++ b/packages/openapi-typescript/test/node-api.test.ts @@ -797,6 +797,11 @@ export interface components { pathItems: never; } export type $defs = Record; +type ReadonlyArray = [ + Exclude +] extends [ + any[] +] ? Readonly> : Readonly[]>; export const pathsUrlGetParametersQueryStatusValues: ReadonlyArray = ["active", "inactive"]; export const statusValues: ReadonlyArray = ["active", "inactive"]; export const errorCodeValues: ReadonlyArray = [100, 101, 102, 103, 104, 105];