Skip to content

Commit 099e4ac

Browse files
Export type instead of interface (optionaly) (#868)
* prepare tests that test flag --export-type that should tell the app to export types instead of interfaces * implement optional exporting type instead of interface * add --export-type flag into cli that is passed as option into the transformer
1 parent 34b9acb commit 099e4ac

14 files changed

+71799
-4
lines changed

bin/cli.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Options
2525
--default-non-nullable (optional) If a schema object has a default value set, don’t mark it as nullable
2626
--prettier-config, -c (optional) specify path to Prettier config file
2727
--raw-schema (optional) Parse as partial schema (raw components)
28+
--export-type (optional) Export type instead of interface
2829
--version (optional) Force schema parsing version
2930
`;
3031

@@ -41,7 +42,7 @@ function errorAndExit(errorMessage) {
4142
const [, , input, ...args] = process.argv;
4243
const flags = parser(args, {
4344
array: ["header"],
44-
boolean: ["defaultNonNullable", "immutableTypes", "rawSchema"],
45+
boolean: ["defaultNonNullable", "immutableTypes", "rawSchema", "exportType"],
4546
number: ["version"],
4647
string: ["auth", "header", "headersObject", "httpMethod", "prettierConfig"],
4748
alias: {
@@ -90,6 +91,7 @@ async function generateSchema(pathToSpec) {
9091
version: flags.version,
9192
httpHeaders,
9293
httpMethod: flags.httpMethod,
94+
exportType: flags.exportType,
9395
});
9496

9597
// output

src/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,17 @@ async function openapiTS(
7676
// 2a. root schema
7777
if (!options?.version && !ctx.rawSchema) ctx.version = swaggerVersion(rootSchema as any); // note: root version cascades down to all subschemas
7878
const rootTypes = transformAll(rootSchema, { ...ctx });
79+
const exportedKind = options.exportType === true ? "type" : "interface";
80+
const exportedKindOperator = options.exportType === true ? " =" : "";
81+
const exportedKindSemicolon = options.exportType === true ? ";" : "";
7982
for (const k of Object.keys(rootTypes)) {
8083
if (typeof rootTypes[k] === "string") {
81-
output += `export interface ${k} {\n ${rootTypes[k]}\n}\n\n`;
84+
output += `export ${exportedKind} ${k}${exportedKindOperator} {\n ${rootTypes[k]}\n}\n\n`;
8285
}
8386
}
8487

8588
// 2b. external schemas (subschemas)
86-
output += `export interface external {\n`;
89+
output += `export ${exportedKind} external${exportedKindOperator} {\n`;
8790
const externalKeys = Object.keys(external);
8891
externalKeys.sort((a, b) => a.localeCompare(b, "en", { numeric: true })); // sort external keys because they may have resolved in a different order each time
8992
for (const subschemaURL of externalKeys) {
@@ -94,7 +97,7 @@ async function openapiTS(
9497
}
9598
output += ` }\n`;
9699
}
97-
output += `}\n\n`;
100+
output += `}${exportedKindSemicolon}\n\n`;
98101

99102
// 3. Prettify
100103
let prettierOptions: prettier.Options = {

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ export interface SwaggerToTSOptions {
151151
* @default {string} GET
152152
*/
153153
httpMethod?: string;
154+
/**
155+
* (optional) Export type instead of interface
156+
*/
157+
exportType?: boolean;
154158
}
155159

156160
/** Context passed to all submodules */
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* This file was auto-generated by openapi-typescript.
3+
* Do not make direct changes to the file.
4+
*/
5+
6+
export type paths = {
7+
"/test": {
8+
get: {
9+
responses: {
10+
/** A list of types. */
11+
200: unknown;
12+
};
13+
};
14+
};
15+
};
16+
17+
export type components = {
18+
schemas: {
19+
/** @description Enum with null and nullable */
20+
MyType: {
21+
/** @enum {string|null} */
22+
myEnumTestFieldNullable?: ("foo" | "bar" | null) | null;
23+
/** @enum {string|null} */
24+
myEnumTestField?: ("foo" | "bar" | null) | null;
25+
/** @constant */
26+
myConstTestField?: "constant-value";
27+
/** @constant */
28+
myConstTestFieldNullable?: 4 | null;
29+
};
30+
};
31+
};
32+
33+
export type operations = {};
34+
35+
export type external = {};

test/v3/expected/github.exported-type.ts

Lines changed: 35713 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/**
2+
* This file was auto-generated by openapi-typescript.
3+
* Do not make direct changes to the file.
4+
*/
5+
6+
export type paths = {
7+
"/contacts": {
8+
get: operations["getContacts"];
9+
};
10+
"/contacts/{userUid}": {
11+
get: operations["getContactInfo"];
12+
};
13+
"/contacts/{userUid}/icon": {
14+
get: operations["getContactIcon"];
15+
};
16+
"/contacts/me": {
17+
get: operations["getMyInfo"];
18+
};
19+
"/contacts/me/icon": {
20+
get: operations["getMyIcon"];
21+
delete: operations["deleteMyIcon"];
22+
};
23+
};
24+
25+
export type components = {
26+
schemas: {
27+
/** Parent of most important objects */
28+
BaseEntity: {
29+
/**
30+
* Format: nanoid
31+
* @deprecated
32+
* @description Test description with deprecated
33+
* @example njbusD52k6YoRG346tPgD
34+
*/
35+
uid?: string;
36+
/**
37+
* Format: date-time
38+
* @description It's date example
39+
* @example 1999-03-31 15:00:00.000
40+
*/
41+
created_at?: string;
42+
/**
43+
* Format: date-time
44+
* @example 2020-07-10 10:10:00.000
45+
*/
46+
updated_at?: string;
47+
deleted?: boolean;
48+
};
49+
/** Image for preview */
50+
Image: {
51+
/** @example https://shantichat.com/data/V1StGXR8_Z5jdHi6B-myT/white-rabbit.png */
52+
url: string;
53+
/** @example 128 */
54+
width: unknown;
55+
/** @example 128 */
56+
height: unknown;
57+
/** @example LEHV6nWB2yk8pyo0adR*.7kCMdnj */
58+
blurhash?: string;
59+
};
60+
/** User object */
61+
User: components["schemas"]["BaseEntity"] & {
62+
/** @example Thomas A. Anderson */
63+
name?: string;
64+
/**
65+
* @default test
66+
* @example The One
67+
*/
68+
description?: string;
69+
icon?: components["schemas"]["Image"];
70+
/** @example America/Chicago */
71+
timezone?: string;
72+
/**
73+
* Format: date-time
74+
* @example 2020-07-10 15:00:00.000
75+
*/
76+
last_online_at?: string;
77+
};
78+
};
79+
};
80+
81+
export type operations = {
82+
getContacts: {
83+
responses: {
84+
/** OK */
85+
200: {
86+
content: {
87+
"application/json": components["schemas"]["User"][];
88+
};
89+
};
90+
};
91+
};
92+
getContactInfo: {
93+
parameters: {
94+
path: {
95+
userUid: string;
96+
};
97+
};
98+
responses: {
99+
/** OK */
100+
200: {
101+
content: {
102+
"application/json": components["schemas"]["User"];
103+
};
104+
};
105+
};
106+
};
107+
getContactIcon: {
108+
parameters: {
109+
path: {
110+
userUid: string;
111+
};
112+
};
113+
responses: {
114+
/** OK */
115+
200: {
116+
content: {
117+
"application/json": components["schemas"]["Image"];
118+
};
119+
};
120+
};
121+
};
122+
getMyInfo: {
123+
responses: {
124+
/** OK */
125+
200: {
126+
content: {
127+
"application/json": components["schemas"]["User"];
128+
};
129+
};
130+
};
131+
};
132+
getMyIcon: {
133+
responses: {
134+
/** OK */
135+
200: {
136+
content: {
137+
"application/json": components["schemas"]["Image"];
138+
};
139+
};
140+
};
141+
};
142+
deleteMyIcon: {
143+
responses: {
144+
/** OK */
145+
200: unknown;
146+
};
147+
};
148+
};
149+
150+
export type external = {};

0 commit comments

Comments
 (0)