Skip to content

Commit 21fb8b0

Browse files
authored
fix(openapi-typescript): type errors generated under enumValues (#1867)
* fix(openapi-typescript): type errors generated under `enumValues` * Create proud-students-repeat.md
1 parent bd88568 commit 21fb8b0

File tree

4 files changed

+31
-2
lines changed

4 files changed

+31
-2
lines changed

.changeset/proud-students-repeat.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-typescript": patch
3+
---
4+
5+
fix(openapi-typescript): type errors generated under `enumValues`

packages/openapi-typescript/src/lib/ts.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,13 @@ export function tsArrayLiteralExpression(
242242
name: string,
243243
elementType: ts.TypeNode,
244244
values: (string | number)[],
245-
options?: { export?: boolean; readonly?: boolean },
245+
options?: { export?: boolean; readonly?: boolean; injectFooter?: ts.Node[] },
246246
) {
247247
let variableName = sanitizeMemberName(name);
248248
variableName = `${variableName[0].toLowerCase()}${variableName.substring(1)}`;
249249

250250
const arrayType = options?.readonly
251-
? ts.factory.createTypeReferenceNode("ReadonlyArray", [elementType])
251+
? tsReadonlyArray(elementType, options.injectFooter)
252252
: ts.factory.createArrayTypeNode(elementType);
253253

254254
return ts.factory.createVariableStatement(
@@ -490,3 +490,21 @@ export function tsWithRequired(
490490
tsUnion(keys.map((k) => tsLiteral(k))),
491491
]);
492492
}
493+
494+
/**
495+
* Enhanced ReadonlyArray.
496+
* eg: type Foo = ReadonlyArray<T>; type Bar = ReadonlyArray<T[]>
497+
* Foo and Bar are both of type `readonly T[]`
498+
*/
499+
export function tsReadonlyArray(type: ts.TypeNode, injectFooter?: ts.Node[]): ts.TypeNode {
500+
if (
501+
injectFooter &&
502+
!injectFooter.some((node) => ts.isTypeAliasDeclaration(node) && node?.name?.escapedText === "ReadonlyArray")
503+
) {
504+
const helper = stringToAST(
505+
"type ReadonlyArray<T> = [Exclude<T, undefined>] extends [any[]] ? Readonly<Exclude<T, undefined>> : Readonly<Exclude<T, undefined>[]>;",
506+
)[0] as any;
507+
injectFooter.push(helper);
508+
}
509+
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("ReadonlyArray"), [type]);
510+
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export function transformSchemaObjectWithComposition(
136136
{
137137
export: true,
138138
readonly: true,
139+
injectFooter: options.ctx.injectFooter,
139140
},
140141
);
141142

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

+5
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,11 @@ export interface components {
797797
pathItems: never;
798798
}
799799
export type $defs = Record<string, never>;
800+
type ReadonlyArray<T> = [
801+
Exclude<T, undefined>
802+
] extends [
803+
any[]
804+
] ? Readonly<Exclude<T, undefined>> : Readonly<Exclude<T, undefined>[]>;
800805
export const pathsUrlGetParametersQueryStatusValues: ReadonlyArray<paths["/url"]["get"]["parameters"]["query"]["status"]> = ["active", "inactive"];
801806
export const statusValues: ReadonlyArray<components["schemas"]["Status"]> = ["active", "inactive"];
802807
export const errorCodeValues: ReadonlyArray<components["schemas"]["ErrorCode"]> = [100, 101, 102, 103, 104, 105];

0 commit comments

Comments
 (0)