From 8618b32422b2cb1b9ed9cad7be96f4383da2d8bb Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:45:41 +0900 Subject: [PATCH 1/2] fix(openapi-typescript): correct generated type when using `prefixItems` --- .../src/transform/schema-object.ts | 45 +++++++++++- packages/openapi-typescript/src/types.ts | 3 +- .../transform/schema-object/array.test.ts | 70 ++++++++++++++++++- 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/packages/openapi-typescript/src/transform/schema-object.ts b/packages/openapi-typescript/src/transform/schema-object.ts index 7e74f094c..902d946e8 100644 --- a/packages/openapi-typescript/src/transform/schema-object.ts +++ b/packages/openapi-typescript/src/transform/schema-object.ts @@ -302,11 +302,50 @@ function transformSchemaObjectCore(schemaObject: SchemaObject, options: Transfor if (schemaObject.type === "array") { // default to `unknown[]` let itemType: ts.TypeNode = UNKNOWN; - // tuple type - if (schemaObject.prefixItems || Array.isArray(schemaObject.items)) { - const prefixItems = schemaObject.prefixItems ?? (schemaObject.items as (SchemaObject | ReferenceObject)[]); + // tuple type using deprecated syntax + if (Array.isArray(schemaObject.items)) { + const prefixItems = schemaObject.items; itemType = ts.factory.createTupleTypeNode(prefixItems.map((item) => transformSchemaObject(item, options))); } + // standard tuple type + else if (schemaObject.prefixItems) { + const prefixItems = schemaObject.prefixItems; + itemType = ts.factory.createTupleTypeNode(prefixItems.map((item) => transformSchemaObject(item, options))); + if (schemaObject.items !== undefined) { + if (schemaObject.items !== false) { + let additionalItemType: ts.TypeNode = UNKNOWN; + if ("type" in schemaObject.items) { + additionalItemType = ts.factory.createArrayTypeNode(transformSchemaObject(schemaObject.items, options)); + } else { + additionalItemType = transformSchemaObject(schemaObject.items, options); + } + itemType = ts.factory.createTupleTypeNode([ + ...(itemType as ts.TupleTypeNode).elements, + ts.factory.createRestTypeNode(additionalItemType), + ]); + } + } else if (schemaObject.unevaluatedItems !== undefined) { + if (schemaObject.unevaluatedItems !== false) { + let additionalItemType: ts.TypeNode = UNKNOWN; + if ("type" in schemaObject.unevaluatedItems) { + additionalItemType = ts.factory.createArrayTypeNode( + transformSchemaObject(schemaObject.unevaluatedItems, options), + ); + } else { + additionalItemType = transformSchemaObject(schemaObject.unevaluatedItems, options); + } + itemType = ts.factory.createTupleTypeNode([ + ...(itemType as ts.TupleTypeNode).elements, + ts.factory.createRestTypeNode(additionalItemType), + ]); + } + } else { + itemType = ts.factory.createTupleTypeNode([ + ...(itemType as ts.TupleTypeNode).elements, + ts.factory.createRestTypeNode(ts.factory.createArrayTypeNode(UNKNOWN)), + ]); + } + } // standard array type else if (schemaObject.items) { if ("type" in schemaObject.items && schemaObject.items.type === "array") { diff --git a/packages/openapi-typescript/src/types.ts b/packages/openapi-typescript/src/types.ts index 73a163973..20acef597 100644 --- a/packages/openapi-typescript/src/types.ts +++ b/packages/openapi-typescript/src/types.ts @@ -483,7 +483,8 @@ export interface IntegerSubtype { export interface ArraySubtype { type: "array" | ["array", "null"]; prefixItems?: (SchemaObject | ReferenceObject)[]; - items?: SchemaObject | ReferenceObject | (SchemaObject | ReferenceObject)[]; + items?: false | SchemaObject | ReferenceObject | (SchemaObject | ReferenceObject)[]; + unevaluatedItems?: false | SchemaObject | ReferenceObject; minItems?: number; maxItems?: number; enum?: (SchemaObject | ReferenceObject)[]; 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..30e99f4cd 100644 --- a/packages/openapi-typescript/test/transform/schema-object/array.test.ts +++ b/packages/openapi-typescript/test/transform/schema-object/array.test.ts @@ -35,7 +35,7 @@ describe("transformSchemaObject > array", () => { }, ], [ - "tuple > prefixItems", + "tuple > prefixItems (open tuple using items)", { given: { type: "array", @@ -45,6 +45,72 @@ describe("transformSchemaObject > array", () => { want: `[ number, number, + number, + ...number[] +]`, + // options: DEFAULT_OPTIONS, + }, + ], + [ + "tuple > prefixItems (closed tuple using unevaluatedItems)", + { + given: { + type: "array", + unevaluatedItems: { type: "number" }, + prefixItems: [{ type: "number" }, { type: "number" }, { type: "number" }], + }, + want: `[ + number, + number, + number, + ...number[] +]`, + // options: DEFAULT_OPTIONS, + }, + ], + [ + "tuple > prefixItems (open tuple not specified)", + { + given: { + type: "array", + prefixItems: [{ type: "number" }, { type: "number" }, { type: "number" }], + }, + want: `[ + number, + number, + number, + ...unknown[] +]`, + // options: DEFAULT_OPTIONS, + }, + ], + [ + "tuple > prefixItems (closed tuple using items)", + { + given: { + type: "array", + items: false, + prefixItems: [{ type: "number" }, { type: "number" }, { type: "number" }], + }, + want: `[ + number, + number, + number +]`, + // options: DEFAULT_OPTIONS, + }, + ], + [ + "tuple > prefixItems (closed tuple using unevaluatedItems)", + { + given: { + type: "array", + unevaluatedItems: false, + prefixItems: [{ type: "number" }, { type: "number" }, { type: "number" }], + }, + want: `[ + number, + number, number ]`, // options: DEFAULT_OPTIONS, @@ -179,7 +245,7 @@ describe("transformSchemaObject > array", () => { { given: { type: "array", - items: { type: "number" }, + items: false, prefixItems: [{ type: "number" }, { type: "number" }, { type: "number" }], }, want: `readonly [ From 909f2147d63769cc657d716ef7a68fb6787f8b88 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:46:05 +0900 Subject: [PATCH 2/2] fix(docs): update incorrect docs --- docs/advanced.md | 3 +-- docs/ja/advanced.md | 3 +-- docs/zh/advanced.md | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/advanced.md b/docs/advanced.md index abf469e7e..0230994d7 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -258,8 +258,7 @@ minItems: 2 ```yaml type: array -items: - type: number +items: false prefixItems: - number - number diff --git a/docs/ja/advanced.md b/docs/ja/advanced.md index 9eb8abb01..33a985a5d 100644 --- a/docs/ja/advanced.md +++ b/docs/ja/advanced.md @@ -258,8 +258,7 @@ minItems: 2 ```yaml type: array -items: - type: number +items: false prefixItems: - number - number diff --git a/docs/zh/advanced.md b/docs/zh/advanced.md index 0b06e5a07..9c834a66b 100644 --- a/docs/zh/advanced.md +++ b/docs/zh/advanced.md @@ -250,8 +250,7 @@ minItems: 2 ```yaml type: array -items: - type: number +items: false prefixItems: - number - number