Skip to content

Commit 89d2ac3

Browse files
committed
Supports minItems / maxItems
fix #871
1 parent fb5e199 commit 89d2ac3

File tree

5 files changed

+59
-2
lines changed

5 files changed

+59
-2
lines changed

bin/cli.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function errorAndExit(errorMessage) {
4242
const [, , input, ...args] = process.argv;
4343
const flags = parser(args, {
4444
array: ["header"],
45-
boolean: ["defaultNonNullable", "immutableTypes", "rawSchema", "exportType"],
45+
boolean: ["defaultNonNullable", "immutableTypes", "rawSchema", "exportType", "supportArrayLength"],
4646
number: ["version"],
4747
string: ["auth", "header", "headersObject", "httpMethod", "prettierConfig"],
4848
alias: {
@@ -92,6 +92,7 @@ async function generateSchema(pathToSpec) {
9292
httpHeaders,
9393
httpMethod: flags.httpMethod,
9494
exportType: flags.exportType,
95+
supportArrayLength: flags.supportArrayLength,
9596
});
9697

9798
// output

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ async function openapiTS(
4141
immutableTypes: options.immutableTypes || false,
4242
rawSchema: options.rawSchema || false,
4343
version: options.version || 3,
44+
supportArrayLength: options.supportArrayLength,
4445
} as any;
4546

4647
// note: we may be loading many large schemas into memory at once; take care to reuse references without cloning

src/transform/schema.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,31 @@ export function transformSchemaObj(node: any, options: TransformSchemaObjOptions
189189
if (Array.isArray(node.items)) {
190190
output += `${readonly}${tsTupleOf(node.items.map((node: any) => transformSchemaObj(node, options)))}`;
191191
} else {
192-
output += `${readonly}${tsArrayOf(node.items ? transformSchemaObj(node.items as any, options) : "unknown")}`;
192+
const minItems: number = Number.isInteger(node.minItems) && node.minItems >= 0 ? node.minItems : 0;
193+
const maxItems: number | undefined =
194+
Number.isInteger(node.maxItems) && node.maxItems >= 0 && minItems <= node.maxItems
195+
? node.maxItems
196+
: undefined;
197+
198+
const estimateCodeSize =
199+
maxItems === undefined ? minItems : (maxItems * (maxItems + 1) - minItems * (minItems - 1)) / 2;
200+
const items = node.items ? transformSchemaObj(node.items as any, options) : "unknown";
201+
if ((minItems !== 0 || maxItems !== undefined) && options.supportArrayLength && estimateCodeSize < 30) {
202+
if (maxItems === undefined) {
203+
output += `${readonly}${tsTupleOf([
204+
...Array.from({ length: minItems }).map(() => items),
205+
`...${tsArrayOf(items)}`,
206+
])}`;
207+
} else {
208+
output += tsUnionOf(
209+
Array.from({ length: maxItems - minItems + 1 })
210+
.map((_, i) => i + minItems)
211+
.map((n) => `${readonly}${tsTupleOf(Array.from({ length: n }).map(() => items))}`)
212+
);
213+
}
214+
} else {
215+
output += `${readonly}${tsArrayOf(items)}`;
216+
}
193217
}
194218
break;
195219
}

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ export interface SwaggerToTSOptions {
155155
* (optional) Export type instead of interface
156156
*/
157157
exportType?: boolean;
158+
supportArrayLength?: boolean;
158159
}
159160

160161
/** Context passed to all submodules */
@@ -169,4 +170,5 @@ export interface GlobalContext {
169170
namespace?: string;
170171
rawSchema: boolean;
171172
version: number;
173+
supportArrayLength?: boolean;
172174
}

test/core/schema.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const defaults = {
1111
required: new Set(),
1212
rawSchema: false,
1313
version: 3,
14+
supportArrayLength: false,
1415
};
1516

1617
describe("SchemaObject", () => {
@@ -150,6 +151,34 @@ describe("SchemaObject", () => {
150151
);
151152
});
152153

154+
it("array (supportArrayLength)", () => {
155+
// (same as above test, but with supportArrayLength: true)
156+
const opts = { ...defaults, supportArrayLength: true };
157+
expect(transform({ type: "array", items: { type: "string" } }, opts)).to.equal(`(string)[]`);
158+
expect(transform({ type: "array", items: { type: "string" }, minItems: 1 }, opts)).to.equal(
159+
`[string, ...(string)[]]`
160+
);
161+
expect(transform({ type: "array", items: { type: "string" }, maxItems: 2 }, opts)).to.equal(
162+
`([]) | ([string]) | ([string, string])`
163+
);
164+
expect(transform({ type: "array", items: { type: "string" }, maxItems: 20 }, opts)).to.equal(`(string)[]`);
165+
});
166+
167+
it("array (immutableTypes, supportArrayLength)", () => {
168+
// (same as above test, but with immutableTypes: true, supportArrayLength: true)
169+
const opts = { ...defaults, immutableTypes: true, supportArrayLength: true };
170+
expect(transform({ type: "array", items: { type: "string" } }, opts)).to.equal(`readonly (string)[]`);
171+
expect(transform({ type: "array", items: { type: "string" }, minItems: 1 }, opts)).to.equal(
172+
`readonly [string, ...(string)[]]`
173+
);
174+
expect(transform({ type: "array", items: { type: "string" }, maxItems: 2 }, opts)).to.equal(
175+
`(readonly []) | (readonly [string]) | (readonly [string, string])`
176+
);
177+
expect(transform({ type: "array", items: { type: "string" }, maxItems: 20 }, opts)).to.equal(
178+
`readonly (string)[]`
179+
);
180+
});
181+
153182
it("enum", () => {
154183
const enumBasic = ["Totoro", "Sats'uki", "Mei"]; // note: also tests quotes in enum
155184
expect(

0 commit comments

Comments
 (0)