-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi-spec.d.ts
More file actions
121 lines (107 loc) · 3.16 KB
/
Copy pathapi-spec.d.ts
File metadata and controls
121 lines (107 loc) · 3.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/** Base type that any api spec should extend. */
export type AnyApiSpec = NonNullable<unknown>
/** Intersection of HTTP methods that are supported by both OpenAPI-TS and MSW. */
export type HttpMethod =
| 'get'
| 'put'
| 'post'
| 'delete'
| 'options'
| 'head'
| 'patch'
// ========== ChatGPT :man_shrugging: ==========
// ========== Typed get/post/patch/etc ==========
type ReplacePathParams<
Path extends string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Params extends Record<string, any>,
> = Path extends `${infer Start}{${infer Param}}${infer Rest}`
? Param extends keyof Params
? `${Start}${Params[Param] extends number
? `${number}`
: Params[Param] extends string
? string
: string}${ReplacePathParams<Rest, Params>}`
: never
: Path
type PathParams<
ApiSpec,
Path extends keyof ApiSpec,
Method extends keyof ApiSpec[Path] = keyof ApiSpec[Path],
> = ApiSpec[Path][Method] extends {
parameters: { path: infer Params }
}
? Params
: object
type TypedPaths<ApiSpec, Method extends string> = {
[Path in keyof ApiSpec & string]: ReplacePathParams<
Path,
PathParams<ApiSpec, Path, Method>
>
}[keyof ApiSpec & string]
export type IsExactMatch<
Actual extends string,
Expected extends string,
> = Actual extends Expected ? (Expected extends Actual ? Actual : never) : never
// ========== Typed reply(statusCode, body, headers) ==========
type TemplateToPattern<Path extends string> =
Path extends `${infer Start}{${string}}${infer Rest}`
? `${Start}${string}${TemplateToPattern<Rest>}`
: Path
type MatchPath<
ApiSpec,
ConcretePath extends string,
Method extends HttpMethod,
> = {
[Tmpl in keyof ApiSpec & string]: ApiSpec[Tmpl] extends Record<
Method,
unknown
>
? ConcretePath extends TemplateToPattern<Tmpl>
? Tmpl
: never
: never
}[keyof ApiSpec & string]
type ExtractResponses<ResponseSpec> = {
[Status in keyof ResponseSpec]: {
body?: ResponseSpec[Status] extends {
content: { 'application/json': infer Body }
}
? Body
: never
headers?: ResponseSpec[Status] extends { headers: infer H } ? H : never
}
}
export type TypedResponse<
ApiSpec,
ConcretePath extends string,
Method extends HttpMethod,
> =
MatchPath<ApiSpec, ConcretePath, Method> extends infer TmplPath
? TmplPath extends keyof ApiSpec
? ApiSpec[TmplPath][Method] extends { responses: infer Resp }
? ExtractResponses<Resp>
: never
: never
: never
type TemplateToPattern<Path extends string> =
Path extends `${infer Start}{${string}}${infer Rest}`
? `${Start}${string}${TemplateToPattern<Rest>}`
: Path
type ExtractResponses<ResponseSpec> = {
[Status in keyof ResponseSpec]: {
body?: ResponseSpec[Status] extends {
content: { 'application/json': infer Body }
}
? Body
: never
headers?: ResponseSpec[Status] extends { headers: infer H } ? H : never
}
}
export type TypedResponseDirect<
ApiSpec,
TmplPath extends keyof ApiSpec & string,
Method extends HttpMethod,
> = ApiSpec[TmplPath][Method] extends { responses: infer Resp }
? ExtractResponses<Resp>
: never