@@ -23,12 +23,18 @@ export type OpArgType<OP> = OP extends {
23
23
}
24
24
// openapi 3
25
25
requestBody ?: {
26
- content : {
27
- 'application/json' : infer RB
28
- }
26
+ content :
27
+ | {
28
+ 'application/json' : infer RB
29
+ }
30
+ | {
31
+ 'multipart/form-data' : infer FD
32
+ }
29
33
}
30
34
}
31
- ? P & Q & ( B extends Record < string , unknown > ? B [ keyof B ] : unknown ) & RB
35
+ ? FD extends Record < string , string >
36
+ ? FormData
37
+ : P & Q & ( B extends Record < string , unknown > ? B [ keyof B ] : unknown ) & RB
32
38
: Record < string , never >
33
39
34
40
type OpResponseTypes < OP > = OP extends {
@@ -65,11 +71,11 @@ export type OpDefaultReturnType<OP> = _OpDefaultReturnType<OpResponseTypes<OP>>
65
71
const never : unique symbol = Symbol ( )
66
72
67
73
type _OpErrorType < T > = {
68
- [ S in Exclude < keyof T , 200 | 201 > ] : {
74
+ [ S in Exclude < keyof T , 200 | 201 | 204 > ] : {
69
75
status : S extends 'default' ? typeof never : S
70
76
data : T [ S ]
71
77
}
72
- } [ Exclude < keyof T , 200 | 201 > ]
78
+ } [ Exclude < keyof T , 200 | 201 | 204 > ]
73
79
74
80
type Coalesce < T , D > = [ T ] extends [ never ] ? D : T
75
81
@@ -236,10 +242,14 @@ function getQuery(
236
242
return queryString ( queryObj )
237
243
}
238
244
239
- function getHeaders ( body ?: string , init ?: HeadersInit ) {
245
+ function getHeaders ( body ?: CustomRequestInit [ 'body' ] , init ?: HeadersInit ) {
240
246
const headers = new Headers ( init )
241
247
242
- if ( body !== undefined && ! headers . has ( 'Content-Type' ) ) {
248
+ if (
249
+ body !== undefined &&
250
+ ! ( body instanceof FormData ) &&
251
+ ! headers . has ( 'Content-Type' )
252
+ ) {
243
253
headers . append ( 'Content-Type' , 'application/json' )
244
254
}
245
255
@@ -250,8 +260,13 @@ function getHeaders(body?: string, init?: HeadersInit) {
250
260
return headers
251
261
}
252
262
253
- function getBody ( method : Method , payload : any ) {
254
- const body = sendBody ( method ) ? JSON . stringify ( payload ) : undefined
263
+ function getBody ( method : Method , payload : unknown ) : CustomRequestInit [ 'body' ] {
264
+ if ( ! sendBody ( method ) ) {
265
+ return
266
+ }
267
+
268
+ const body = payload instanceof FormData ? payload : JSON . stringify ( payload )
269
+
255
270
// if delete don't send body if empty
256
271
return method === 'delete' && body === '{}' ? undefined : body
257
272
}
@@ -272,7 +287,10 @@ function mergeRequestInit(
272
287
return { ...first , ...second , headers }
273
288
}
274
289
275
- function getFetchParams ( request : Request ) {
290
+ function getFetchParams ( request : Request ) : {
291
+ url : string
292
+ init : CustomRequestInit
293
+ } {
276
294
// clone payload
277
295
// if body is a top level array [ 'a', 'b', param: value ] with param values
278
296
// using spread [ ...payload ] returns [ 'a', 'b' ] and skips custom keys
@@ -288,7 +306,8 @@ function getFetchParams(request: Request) {
288
306
const headers = getHeaders ( body , request . init ?. headers )
289
307
const url = request . baseUrl + path + query
290
308
291
- const init = {
309
+ // @ts -expect-error `body` is the correct type, but because we're using exact optional types, `body` is not allowed to be explicitly `undefined`. It's inferred as `undefined` because of the union return type of `getBody`, and there's no way to tell TS that it's optional here.
310
+ const init : CustomRequestInit = {
292
311
...request . init ,
293
312
method : request . method . toUpperCase ( ) ,
294
313
headers,
@@ -429,7 +448,7 @@ function fetcher<Paths>() {
429
448
init : mergeRequestInit ( defaultInit , init ) ,
430
449
fetch,
431
450
} ) ,
432
- ) ) as CreateFetch < M , Paths [ P ] [ M ] > ,
451
+ ) ) as unknown as CreateFetch < M , Paths [ P ] [ M ] > ,
433
452
} ) ,
434
453
} ) ,
435
454
}
0 commit comments