1
1
import type { OpenAPIV3 } from "openapi-types" ;
2
- import { sanitizePropertyName , sanitizeTypeName } from "../utils" ;
2
+ import { getTypeFromSchema , pascalCase , sanitizePropertyName , sanitizeTypeName } from "../utils" ;
3
3
4
4
interface SchemaContext {
5
5
schemas : { [ key : string ] : OpenAPIV3 . SchemaObject } ;
6
6
generatedTypes : Set < string > ;
7
7
}
8
8
9
- /**
10
- * Converts OpenAPI schema type to TypeScript type
11
- */
12
- function getTypeFromSchema (
13
- schema : OpenAPIV3 . SchemaObject | OpenAPIV3 . ReferenceObject ,
14
- context : SchemaContext
15
- ) : string {
16
- if ( ! schema ) return "any" ;
17
-
18
- if ( "$ref" in schema ) {
19
- const refType = schema . $ref . split ( "/" ) . pop ( ) ;
20
- return sanitizeTypeName ( refType as string ) ;
21
- }
22
- const nullable = schema . nullable ? " | null" : "" ;
23
-
24
- // Handle enum types properly
25
- if ( schema . enum ) {
26
- return schema . enum . map ( ( e ) => ( typeof e === "string" ? `'${ e } '` : e ) ) . join ( " | " ) + nullable ;
27
- }
28
-
29
- switch ( schema . type ) {
30
- case "string" :
31
- if ( "format" in schema && schema . format === "binary" ) {
32
- return `string | { name?: string; type?: string; uri: string }${ nullable } ` ;
33
- }
34
-
35
- return `string${ nullable } ` ;
36
- case "number" :
37
- case "integer" :
38
- return `number${ nullable } ` ;
39
- case "boolean" :
40
- return `boolean${ nullable } ` ;
41
- case "array" : {
42
- const itemType = getTypeFromSchema ( schema . items , context ) ;
43
- return `Array<${ itemType } >${ nullable } ` ;
44
- }
45
- case "object" :
46
- if ( schema . properties ) {
47
- const properties = Object . entries ( schema . properties )
48
- . map ( ( [ key , prop ] ) => {
49
- const isRequired = schema . required ?. includes ( key ) ;
50
- const propertyType = getTypeFromSchema ( prop , context ) ;
51
- const safeName = sanitizePropertyName ( key ) ;
52
- return ` ${ safeName } ${ isRequired ? "" : "?" } : ${ propertyType } ;` ;
53
- } )
54
- . join ( "\n" ) ;
55
- return `{${ properties } \n}${ nullable } ` ;
56
- }
57
- if ( schema . additionalProperties ) {
58
- const valueType =
59
- typeof schema . additionalProperties === "boolean"
60
- ? "any"
61
- : getTypeFromSchema ( schema . additionalProperties , context ) ;
62
- return `Record<string, ${ valueType } >${ nullable } ` ;
63
- }
64
- return `Record<string, any>${ nullable } ` ;
65
- default :
66
- return `any${ nullable } ` ;
67
- }
68
- }
69
-
70
9
function generateTypeDefinition (
71
10
name : string ,
72
- schema : OpenAPIV3 . SchemaObject | OpenAPIV3 . ReferenceObject ,
73
- context : SchemaContext
11
+ schema : OpenAPIV3 . SchemaObject | OpenAPIV3 . ReferenceObject
74
12
) : string {
75
13
const description = ! ( "$ref" in schema ) && schema . description ? `/**\n * ${ schema . description } \n */\n` : "" ;
76
- const typeValue = getTypeFromSchema ( schema , context ) ;
14
+ const typeValue = getTypeFromSchema ( schema ) ;
77
15
78
16
// Use 'type' for primitives, unions, and simple types
79
17
// Use 'interface' only for complex objects with properties
@@ -98,7 +36,7 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
98
36
// Generate types for all schema definitions
99
37
for ( const [ name , schema ] of Object . entries ( context . schemas ) ) {
100
38
if ( context . generatedTypes . has ( name ) ) continue ;
101
- output += generateTypeDefinition ( name , schema , context ) ;
39
+ output += generateTypeDefinition ( name , schema ) ;
102
40
context . generatedTypes . add ( name ) ;
103
41
}
104
42
@@ -110,36 +48,73 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
110
48
111
49
const operationObject = operation as OpenAPIV3 . OperationObject ;
112
50
if ( ! operationObject ) continue ;
113
- const operationId = `${ sanitizeTypeName ( operationObject . operationId || `${ path . replace ( / \W + / g, "_" ) } ` ) } ` ;
51
+ const { operationId : badOperationId , requestBody, responses, parameters } = operationObject ;
52
+ const operationId = `${ sanitizeTypeName ( badOperationId || `${ path . replace ( / \W + / g, "_" ) } ` ) } ` ;
114
53
115
54
// Generate request body type
116
- if ( operationObject . requestBody ) {
117
- const content = ( operationObject . requestBody as OpenAPIV3 . RequestBodyObject ) . content ;
55
+ if ( requestBody ) {
56
+ const content = ( requestBody as OpenAPIV3 . RequestBodyObject ) . content ;
118
57
const jsonContent =
119
58
content [ "application/ld+json" ] ??
120
59
content [ "application/json" ] ??
121
60
content [ "multipart/form-data" ] ??
122
61
content [ "application/octet-stream" ] ;
123
62
if ( jsonContent ?. schema ) {
124
63
const typeName = `${ operationId } Request` ;
125
- output += generateTypeDefinition ( typeName , jsonContent . schema as OpenAPIV3 . SchemaObject , context ) ;
64
+ output += generateTypeDefinition ( typeName , jsonContent . schema as OpenAPIV3 . SchemaObject ) ;
126
65
}
127
66
}
128
67
129
68
// Generate response types
130
- if ( operationObject . responses ) {
131
- for ( const [ code , response ] of Object . entries ( operationObject . responses ) ) {
69
+ if ( responses ) {
70
+ for ( const [ code , response ] of Object . entries ( responses ) ) {
132
71
const responseObj = response as OpenAPIV3 . ResponseObject ;
133
72
const content =
134
73
responseObj . content ?. [ "application/ld+json" ] ??
135
74
responseObj . content ?. [ "application/json" ] ??
136
75
responseObj . content ?. [ "application/octet-stream" ] ;
137
76
if ( content ?. schema ) {
138
77
const typeName = `${ operationId } Response${ code } ` ;
139
- output += generateTypeDefinition ( typeName , content . schema as OpenAPIV3 . SchemaObject , context ) ;
78
+ output += generateTypeDefinition ( typeName , content . schema as OpenAPIV3 . SchemaObject ) ;
140
79
}
141
80
}
142
81
}
82
+
83
+ // Build data type parts
84
+ const dataProps : string [ ] = [ ] ;
85
+
86
+ const urlParams = ( parameters ?. filter ( ( p ) => "in" in p && p . in === "path" ) ||
87
+ [ ] ) as OpenAPIV3 . ParameterObject [ ] ;
88
+ const queryParams = ( parameters ?. filter ( ( p ) => "in" in p && p . in === "query" ) ||
89
+ [ ] ) as OpenAPIV3 . ParameterObject [ ] ;
90
+
91
+ // Add path and query parameters
92
+ urlParams . forEach ( ( p ) => {
93
+ const safeName = sanitizePropertyName ( p . name ) ;
94
+ dataProps . push ( `${ safeName } : ${ getTypeFromSchema ( p . schema ) } ` ) ;
95
+ } ) ;
96
+ queryParams . forEach ( ( p ) => {
97
+ const safeName = sanitizePropertyName ( p . name ) ;
98
+ dataProps . push ( `${ safeName } ${ p . required ? "" : "?" } : ${ getTypeFromSchema ( p . schema ) } ` ) ;
99
+ } ) ;
100
+
101
+ // Add request body type if it exists
102
+ const hasData = ( parameters && parameters . length > 0 ) || requestBody ;
103
+
104
+ let dataType = "undefined" ;
105
+ const namedType = pascalCase ( operationId ) ;
106
+ if ( hasData ) {
107
+ if ( requestBody && dataProps . length > 0 ) {
108
+ dataType = `${ namedType } Request & { ${ dataProps . join ( "; " ) } }` ;
109
+ } else if ( requestBody ) {
110
+ dataType = `${ namedType } Request` ;
111
+ } else if ( dataProps . length > 0 ) {
112
+ dataType = `{ ${ dataProps . join ( "; " ) } }` ;
113
+ } else {
114
+ dataType = "Record<string, never>" ;
115
+ }
116
+ output += `\n\nexport type ${ pascalCase ( operationId ) } Params = ${ dataType } ;\n\n` ;
117
+ }
143
118
}
144
119
}
145
120
}
0 commit comments