1
- import { IRouter , RequestHandler } from "express" ;
1
+ import { IRouter , RequestHandler , Router } from "express" ;
2
2
import {
3
3
Method ,
4
4
AnyApiResponses ,
5
5
ApiResBody ,
6
6
ApiSpec ,
7
- AnyApiSpec ,
8
7
AnyApiEndpoints ,
9
8
} from "../index" ;
10
9
import {
@@ -15,9 +14,14 @@ import {
15
14
} from "express-serve-static-core" ;
16
15
import { StatusCode } from "../core" ;
17
16
import { ParsedQs } from "qs" ;
18
- import { AnySpecValidator , SpecValidatorMap } from "../core/validator/request" ;
17
+ import { AnySpecValidator } from "../core/validator/request" ;
19
18
import { StandardSchemaV1 } from "@standard-schema/spec" ;
20
- import { newSSValidator , SSApiEndpoints } from "../core/ss" ;
19
+ import {
20
+ newSSValidator ,
21
+ SSApiEndpoints ,
22
+ ToApiEndpoints ,
23
+ ToSSValidators ,
24
+ } from "../core/ss" ;
21
25
22
26
/**
23
27
* Express Request Handler, but with more strict type information.
@@ -37,23 +41,64 @@ export type Handler<
37
41
) => void ;
38
42
39
43
export type ToHandler <
40
- Spec extends AnyApiSpec | undefined ,
41
- Validators extends AnySpecValidator | undefined ,
44
+ E extends SSApiEndpoints ,
45
+ Path extends keyof E & string ,
46
+ M extends Method ,
42
47
> = Handler <
43
- Spec ,
48
+ ToApiEndpoints < E > [ Path ] [ M ] ,
44
49
ValidateLocals <
45
- Validators extends AnySpecValidator ? Validators : Record < string , never >
50
+ ToSSValidators < E , Path , M > extends AnySpecValidator
51
+ ? ToSSValidators < E , Path , M >
52
+ : Record < string , never >
46
53
>
47
54
> ;
55
+ // export type ToHandler<
56
+ // Spec extends AnyApiSpec | undefined,
57
+ // Validators extends AnySpecValidator | undefined,
58
+ // > = Handler<
59
+ // Spec,
60
+ // ValidateLocals<
61
+ // Validators extends AnySpecValidator ? Validators : Record<string, never>
62
+ // >
63
+ // >;
48
64
49
- export type ToHandlers <
50
- E extends AnyApiEndpoints ,
51
- V extends SpecValidatorMap ,
52
- > = {
65
+ /**
66
+ * Convert ZodApiSpec to Express Request Handler type.
67
+ */
68
+ // export type ToHandler<
69
+ // ZodE extends SSApiEndpoints,
70
+ // Path extends keyof ZodE & string,
71
+ // M extends Method,
72
+ // > = ToPureHandler<ToApiEndpoints<ZodE>[Path][M], ToSSValidators<ZodE, Path, M>>;
73
+
74
+ export type ToHandlers < E extends SSApiEndpoints > = {
53
75
[ Path in keyof E & string ] : {
54
- [ M in Method ] : ToHandler < E [ Path ] [ M ] , V [ Path ] [ M ] > ;
76
+ [ M in Method ] : ToHandler < E , Path , M > ;
55
77
} ;
56
78
} ;
79
+ // export type ToHandlers<
80
+ // E extends AnyApiEndpoints,
81
+ // V extends SpecValidatorMap,
82
+ // > = {
83
+ // [Path in keyof E & string]: {
84
+ // [M in Method]: ToHandler<E[Path][M], V[Path][M]>;
85
+ // };
86
+ // };
87
+
88
+ /**
89
+ * Convert SSApiEndpoints to Express Request Handler type map.
90
+ */
91
+ // export type ToHandlers<
92
+ // ZodE extends SSApiEndpoints,
93
+ // E extends ToApiEndpoints<ZodE> = ToApiEndpoints<ZodE>,
94
+ // V extends ToValidatorsMap<ZodE> = ToValidatorsMap<ZodE>,
95
+ // > = ToPureHandlers<E, V>;
96
+
97
+ // export type ToValidatorsMap<ESchema extends SSApiEndpoints> = {
98
+ // [Path in keyof ESchema & string]: {
99
+ // [M in Method]: ToSSValidators<ESchema, Path, M>;
100
+ // };
101
+ // };
57
102
58
103
/**
59
104
* Express Response, but with more strict type information.
@@ -80,7 +125,6 @@ export type ValidateLocals<
80
125
*/
81
126
export type RouterT <
82
127
E extends AnyApiEndpoints ,
83
- V extends SpecValidatorMap ,
84
128
SC extends StatusCode = StatusCode ,
85
129
> = Omit < IRouter , Method > & {
86
130
[ M in Method ] : < Path extends string & keyof E > (
@@ -89,9 +133,9 @@ export type RouterT<
89
133
// Middlewareは複数のエンドポイントで実装を使い回されることがあるので、型チェックはゆるくする
90
134
...Array < RequestHandler > ,
91
135
// Handlerは厳密に型チェックする
92
- ToHandler < E [ Path ] [ M ] , V [ Path ] [ M ] > ,
136
+ ToHandler < E , Path , M > ,
93
137
]
94
- ) => RouterT < E , V , SC > ;
138
+ ) => RouterT < E , SC > ;
95
139
} ;
96
140
97
141
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -212,3 +256,29 @@ export const asAsync = <Router extends IRouter | RouterT<any, any>>(
212
256
} ,
213
257
} ) ;
214
258
} ;
259
+
260
+ /**
261
+ * Set validator and add more strict type information to router.
262
+ *
263
+ * @param pathMap API endpoints
264
+ * @param router Express Router
265
+ *
266
+ * @example
267
+ * ```
268
+ * const router = typed(pathMap, express.Router())
269
+ * router.get('/path', (req, res) => {
270
+ * const {data, error} = res.locals.validate(req).query()
271
+ * if (error) {
272
+ * return res.status(400).json({ message: 'Invalid query' })
273
+ * }
274
+ * return res.status(200).json({ message: 'success', value: r.data.value })
275
+ * })
276
+ * ```
277
+ */
278
+ export const typed = < const Endpoints extends SSApiEndpoints > (
279
+ pathMap : Endpoints ,
280
+ router : Router ,
281
+ ) : RouterT < Endpoints > => {
282
+ router . use ( validatorMiddleware ( pathMap ) ) ;
283
+ return router ;
284
+ } ;
0 commit comments