Skip to content

Commit 95aa4cb

Browse files
author
Loïc Fürhoff
authored
Add type helpers only when used (#992)
1 parent 477d9a6 commit 95aa4cb

File tree

4 files changed

+68
-22
lines changed

4 files changed

+68
-22
lines changed

examples/octokit-ghes-3.6-diff-to-api.ts

-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
*/
55

66

7-
/** Type helpers */
8-
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
9-
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
10-
type OneOf<T extends any[]> = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR<A, B>, ...Rest]> : never;
11-
127
export interface paths {
138
"/admin/hooks": {
149
/** List global webhooks */

examples/stripe-api.ts

-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
*/
55

66

7-
/** Type helpers */
8-
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
9-
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
10-
type OneOf<T extends any[]> = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR<A, B>, ...Rest]> : never;
11-
127
export interface paths {
138
"/v1/account": {
149
/** @description <p>Retrieves the details of an account.</p> */

src/index.ts

+16-12
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,10 @@ async function openapiTS(
9494
output.push(COMMENT_HEADER);
9595
}
9696

97-
// 2b. OneOf helper (@see https://github.com/Microsoft/TypeScript/issues/14094#issuecomment-723571692)
98-
output.push(
99-
"/** Type helpers */",
100-
"type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };",
101-
"type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;",
102-
"type OneOf<T extends any[]> = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR<A, B>, ...Rest]> : never;",
103-
""
104-
);
105-
106-
// 2c. options.inject
97+
// 2b. options.inject
10798
if (options.inject) output.push(options.inject);
10899

109-
// 2d. root schema
100+
// 2c. root schema
110101
const rootTypes = transformSchema(allSchemas["."].schema as OpenAPI3, ctx);
111102
for (const k of Object.keys(rootTypes)) {
112103
if (rootTypes[k] && !EMPTY_OBJECT_RE.test(rootTypes[k])) {
@@ -121,7 +112,7 @@ async function openapiTS(
121112
delete allSchemas["."]; // garbage collect, but also remove from next step (external)
122113
}
123114

124-
// 2e. external schemas (subschemas)
115+
// 2d. external schemas (subschemas)
125116
const externalKeys = Object.keys(allSchemas); // root schema (".") should already be removed
126117
if (externalKeys.length) {
127118
let indentLv = 0;
@@ -198,6 +189,19 @@ async function openapiTS(
198189
output.push(`export type operations = Record<string, never>;`, "");
199190
}
200191

192+
// 4. OneOf type helper (@see https://github.com/Microsoft/TypeScript/issues/14094#issuecomment-723571692)
193+
if (output.join("\n").includes("OneOf")) {
194+
output.splice(
195+
1,
196+
0,
197+
"/** Type helpers */",
198+
"type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };",
199+
"type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;",
200+
"type OneOf<T extends any[]> = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR<A, B>, ...Rest]> : never;",
201+
""
202+
);
203+
}
204+
201205
return output.join("\n");
202206
}
203207

test/index.test.ts

+52
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ const BOILERPLATE = `/**
77
* Do not make direct changes to the file.
88
*/
99
10+
`;
1011

12+
const TYPE_HELPERS = `
1113
/** Type helpers */
1214
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
1315
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
@@ -442,6 +444,56 @@ export interface components {
442444
443445
export type external = Record<string, never>;
444446
447+
export type operations = Record<string, never>;
448+
`);
449+
});
450+
});
451+
452+
describe("OneOf type helpers", () => {
453+
test("should be added only when used", async () => {
454+
const generated = await openapiTS(
455+
{
456+
openapi: "3.1",
457+
info: { title: "Test", version: "1.0" },
458+
components: {
459+
schemas: {
460+
User: {
461+
oneOf: [
462+
{
463+
type: "object",
464+
properties: { firstName: { type: "string" } },
465+
},
466+
{
467+
type: "object",
468+
properties: { name: { type: "string" } },
469+
},
470+
],
471+
},
472+
},
473+
},
474+
},
475+
{ exportType: false }
476+
);
477+
expect(generated).toBe(`${BOILERPLATE}${TYPE_HELPERS}
478+
export type paths = Record<string, never>;
479+
480+
export interface components {
481+
schemas: {
482+
User: OneOf<[{
483+
firstName?: string;
484+
}, {
485+
name?: string;
486+
}]>;
487+
};
488+
responses: never;
489+
parameters: never;
490+
requestBodies: never;
491+
headers: never;
492+
pathItems: never;
493+
}
494+
495+
export type external = Record<string, never>;
496+
445497
export type operations = Record<string, never>;
446498
`);
447499
});

0 commit comments

Comments
 (0)