Skip to content

Commit

Permalink
Simplify minItems / maxItems tuple generation
Browse files Browse the repository at this point in the history
  • Loading branch information
duncanbeevers committed Dec 16, 2024
1 parent 713ea1b commit da20fc9
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-phones-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-typescript": patch
---

Simplify minItems / maxItems tuple generation.
40 changes: 13 additions & 27 deletions packages/openapi-typescript/src/transform/schema-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,40 +339,26 @@ function transformSchemaObjectCore(schemaObject: SchemaObject, options: Transfor
typeof schemaObject.maxItems === "number" && schemaObject.maxItems >= 0 && min <= schemaObject.maxItems
? schemaObject.maxItems
: undefined;
const estimateCodeSize = typeof max !== "number" ? min : (max * (max + 1) - min * (min - 1)) / 2;
const estimateCodeSize = max === undefined ? min : (max * (max + 1) - min * (min - 1)) / 2;
if (
options.ctx.arrayLength &&
(min !== 0 || max !== undefined) &&
estimateCodeSize < 30 // "30" is an arbitrary number but roughly around when TS starts to struggle with tuple inference in practice
) {
if (min === max) {
const elements: ts.TypeNode[] = [];
for (let i = 0; i < min; i++) {
elements.push(itemType);
}
return tsUnion([ts.factory.createTupleTypeNode(elements)]);
} else if ((schemaObject.maxItems as number) > 0) {
// if maxItems is set, then return a union of all permutations of possible tuple types
const members: ts.TypeNode[] = [];
// populate 1 short of min …
for (let i = 0; i <= (max ?? 0) - min; i++) {
const elements: ts.TypeNode[] = [];
for (let j = min; j < i + min; j++) {
elements.push(itemType);
}
members.push(ts.factory.createTupleTypeNode(elements));
}
return tsUnion(members);
}
// if maxItems not set, then return a simple tuple type the length of `min`
else {
const elements: ts.TypeNode[] = [];
for (let i = 0; i < min; i++) {
elements.push(itemType);
}
elements.push(ts.factory.createRestTypeNode(ts.factory.createArrayTypeNode(itemType)));
return ts.factory.createTupleTypeNode(elements);
if (max === undefined) {
return ts.factory.createTupleTypeNode([
...Array.from({ length: min }).map(() => itemType),
ts.factory.createRestTypeNode(ts.factory.createArrayTypeNode(itemType)),
]);
}

// if maxItems is set, then return a union of all permutations of possible tuple types
return tsUnion(
Array.from({ length: max === undefined ? min : max - min + 1 }).map((_, index) =>
ts.factory.createTupleTypeNode(Array.from({ length: index + min }).map(() => itemType)),
),
);
}

const finalType =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@ describe("transformSchemaObject > array", () => {
] | [
string,
string
]`,
options: {
...DEFAULT_OPTIONS,
ctx: { ...DEFAULT_OPTIONS.ctx, arrayLength: true },
},
},
],
[
"options > arrayLength: true > minItems: 1, maxItems: 2",
{
given: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 2 },
want: `[
string
] | [
string,
string
]`,
options: {
...DEFAULT_OPTIONS,
Expand Down

0 comments on commit da20fc9

Please sign in to comment.