From da20fc94c33649f5ba26651ea3c2841ba1f9dfac Mon Sep 17 00:00:00 2001 From: Duncan Beevers Date: Mon, 16 Dec 2024 08:03:00 -0800 Subject: [PATCH] Simplify minItems / maxItems tuple generation Closes #2048 --- .changeset/clean-phones-deliver.md | 5 +++ .../src/transform/schema-object.ts | 40 ++++++------------- .../transform/schema-object/array.test.ts | 16 ++++++++ 3 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 .changeset/clean-phones-deliver.md diff --git a/.changeset/clean-phones-deliver.md b/.changeset/clean-phones-deliver.md new file mode 100644 index 000000000..878466f42 --- /dev/null +++ b/.changeset/clean-phones-deliver.md @@ -0,0 +1,5 @@ +--- +"openapi-typescript": patch +--- + +Simplify minItems / maxItems tuple generation. diff --git a/packages/openapi-typescript/src/transform/schema-object.ts b/packages/openapi-typescript/src/transform/schema-object.ts index 80ec56b51..6b9836a00 100644 --- a/packages/openapi-typescript/src/transform/schema-object.ts +++ b/packages/openapi-typescript/src/transform/schema-object.ts @@ -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 = diff --git a/packages/openapi-typescript/test/transform/schema-object/array.test.ts b/packages/openapi-typescript/test/transform/schema-object/array.test.ts index 59a2fddb7..8b48b0b0e 100644 --- a/packages/openapi-typescript/test/transform/schema-object/array.test.ts +++ b/packages/openapi-typescript/test/transform/schema-object/array.test.ts @@ -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,