Skip to content

Commit bd43a6e

Browse files
committed
Use named type parameters in fetch-router
This is a start on #10805
1 parent bfb5a68 commit bd43a6e

File tree

10 files changed

+181
-172
lines changed

10 files changed

+181
-172
lines changed

packages/fetch-router/src/lib/app-storage.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class AppStorage {
1010
* @param key The key to check
1111
* @returns `true` if a value is stored for the given key, `false` otherwise
1212
*/
13-
has<K extends StorageKey<any>>(key: K): boolean {
13+
has<key extends StorageKey<any>>(key: key): boolean {
1414
return this.#map.has(key)
1515
}
1616

@@ -20,7 +20,7 @@ export class AppStorage {
2020
* @param key The key to get
2121
* @returns The value for the given key
2222
*/
23-
get<K extends StorageKey<any>>(key: K): StorageValue<K> {
23+
get<key extends StorageKey<any>>(key: key): StorageValue<key> {
2424
if (!this.#map.has(key)) {
2525
if (key.defaultValue === undefined) {
2626
throw new Error(`Missing default value in storage for key ${key}`)
@@ -29,7 +29,7 @@ export class AppStorage {
2929
return key.defaultValue
3030
}
3131

32-
return this.#map.get(key) as StorageValue<K>
32+
return this.#map.get(key) as StorageValue<key>
3333
}
3434

3535
/**
@@ -38,15 +38,15 @@ export class AppStorage {
3838
* @param key The key to set
3939
* @param value The value to set
4040
*/
41-
set<K extends StorageKey<any>>(key: K, value: StorageValue<K>): void {
41+
set<key extends StorageKey<any>>(key: key, value: StorageValue<key>): void {
4242
this.#map.set(key, value)
4343
}
4444
}
4545

4646
/**
4747
* Create a storage key with an optional default value.
4848
*
49-
* @param defaultValue The default value for the storage key
49+
* @param T The default value for the storage key
5050
* @returns The new storage key
5151
*/
5252
export function createStorageKey<T>(defaultValue?: T): StorageKey<T> {
@@ -57,4 +57,4 @@ export interface StorageKey<T> {
5757
defaultValue?: T
5858
}
5959

60-
export type StorageValue<TKey> = TKey extends StorageKey<infer T> ? T : never
60+
export type StorageValue<T> = T extends StorageKey<infer V> ? V : never

packages/fetch-router/src/lib/form-action.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,35 +26,35 @@ export interface FormActionOptions {
2626
*
2727
* @param pattern The route pattern to use for the form and its submit action
2828
*/
29-
export function createFormAction<P extends string, const O extends FormActionOptions>(
30-
pattern: P | RoutePattern<P>,
31-
options?: O,
32-
): BuildFormActionMap<P, O> {
29+
export function createFormAction<pattern extends string, const options extends FormActionOptions>(
30+
pattern: pattern | RoutePattern<pattern>,
31+
options?: options,
32+
): BuildFormActionMap<pattern, options> {
3333
let formMethod = options?.formMethod ?? 'POST'
3434
let indexName = options?.names?.index ?? 'index'
3535
let actionName = options?.names?.action ?? 'action'
3636

3737
return createRoutes(pattern, {
3838
[indexName]: { method: 'GET', pattern: '/' },
3939
[actionName]: { method: formMethod, pattern: '/' },
40-
}) as BuildFormActionMap<P, O>
40+
}) as BuildFormActionMap<pattern, options>
4141
}
4242

4343
// prettier-ignore
44-
type BuildFormActionMap<P extends string, O extends FormActionOptions> = BuildRouteMap<
45-
P,
44+
type BuildFormActionMap<pattern extends string, options extends FormActionOptions> = BuildRouteMap<
45+
pattern,
4646
{
4747
[
48-
K in O extends { names: { index: infer N extends string } } ? N : 'index'
48+
key in options extends { names: { index: infer indexName extends string } } ? indexName : 'index'
4949
]: {
5050
method: 'GET'
5151
pattern: '/'
5252
}
5353
} & {
5454
[
55-
K in O extends { names: { action: infer N extends string } } ? N : 'action'
55+
key in options extends { names: { action: infer actionName extends string } } ? actionName : 'action'
5656
]: {
57-
method: O extends { formMethod: infer M extends RequestMethod } ? M : 'POST'
57+
method: options extends { formMethod: infer formMethod extends RequestMethod } ? formMethod : 'POST'
5858
pattern: '/'
5959
}
6060
}

packages/fetch-router/src/lib/middleware.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@ import type { RequestMethod } from './request-methods.ts'
88
* to the next middleware or request handler in the chain.
99
*/
1010
export interface Middleware<
11-
Method extends RequestMethod | 'ANY' = RequestMethod | 'ANY',
12-
Params extends Record<string, any> = {},
11+
method extends RequestMethod | 'ANY' = RequestMethod | 'ANY',
12+
params extends Record<string, any> = {},
1313
> {
1414
(
15-
context: RequestContext<Method, Params>,
15+
context: RequestContext<method, params>,
1616
next: NextFunction,
1717
): Response | undefined | void | Promise<Response | undefined | void>
1818
}
1919

2020
export type NextFunction = () => Promise<Response>
2121

2222
export function runMiddleware<
23-
Method extends RequestMethod | 'ANY' = RequestMethod | 'ANY',
24-
Params extends Record<string, any> = {},
23+
method extends RequestMethod | 'ANY' = RequestMethod | 'ANY',
24+
params extends Record<string, any> = {},
2525
>(
26-
middleware: Middleware<Method, Params>[],
27-
context: RequestContext<Method, Params>,
28-
handler: RequestHandler<Method, Params, Response>,
26+
middleware: Middleware<method, params>[],
27+
context: RequestContext<method, params>,
28+
handler: RequestHandler<method, params, Response>,
2929
): Promise<Response> {
3030
let index = -1
3131

packages/fetch-router/src/lib/request-context.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import type { RequestBodyMethod, RequestMethod } from './request-methods.ts'
88
* handler or middleware in the lifecycle of a request receives the same context object.
99
*/
1010
export class RequestContext<
11-
Method extends RequestMethod | 'ANY' = RequestMethod | 'ANY',
12-
Params extends Record<string, any> = {},
11+
method extends RequestMethod | 'ANY' = RequestMethod | 'ANY',
12+
params extends Record<string, any> = {},
1313
> {
1414
/**
1515
* Parsed [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) from the
@@ -19,7 +19,7 @@ export class RequestContext<
1919
*
2020
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
2121
*/
22-
formData: Method extends RequestBodyMethod ? FormData : FormData | undefined
22+
formData: method extends RequestBodyMethod ? FormData : FormData | undefined
2323
/**
2424
* The request method. This may differ from `request.method` if the request body contained a
2525
* method override field (e.g. `_method=DELETE`), allowing HTML forms to simulate RESTful API
@@ -29,7 +29,7 @@ export class RequestContext<
2929
/**
3030
* Params that were parsed from the URL.
3131
*/
32-
params: Params
32+
params: params
3333
/**
3434
* The original request that was dispatched to the router.
3535
*/
@@ -53,7 +53,7 @@ export class RequestContext<
5353
constructor(request: Request) {
5454
this.formData = undefined as any
5555
this.method = request.method.toUpperCase() as RequestMethod
56-
this.params = {} as Params
56+
this.params = {} as params
5757
this.request = request
5858
this.storage = new AppStorage()
5959
this.headers = new SuperHeaders(request.headers)

packages/fetch-router/src/lib/resource.ts

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export interface ResourceOptions {
3232
* @param base The base route pattern to use for the resource
3333
* @param options Options to configure the resource routes
3434
*/
35-
export function createResource<P extends string, const O extends ResourceOptions>(
36-
base: P | RoutePattern<P>,
37-
options?: O,
38-
): BuildResourceMap<P, O> {
35+
export function createResource<base extends string, const options extends ResourceOptions>(
36+
base: base | RoutePattern<base>,
37+
options?: options,
38+
): BuildResourceMap<base, options> {
3939
let only = options?.only ?? (ResourceMethods as readonly ResourceMethod[])
4040
let newName = options?.names?.new ?? 'new'
4141
let showName = options?.names?.show ?? 'show'
@@ -65,25 +65,28 @@ export function createResource<P extends string, const O extends ResourceOptions
6565
routes[destroyName] = { method: 'DELETE', pattern: `/` }
6666
}
6767

68-
return createRoutes(base, routes) as BuildResourceMap<P, O>
68+
return createRoutes(base, routes) as BuildResourceMap<base, options>
6969
}
7070

71-
type BuildResourceMap<B extends string, O extends ResourceOptions> = BuildRouteMap<
72-
B,
71+
type BuildResourceMap<base extends string, options extends ResourceOptions> = BuildRouteMap<
72+
base,
7373
BuildResourceRoutes<
74-
O,
75-
O extends { only: readonly ResourceMethod[] } ? O['only'][number] : ResourceMethod
74+
options,
75+
options extends { only: readonly ResourceMethod[] } ? options['only'][number] : ResourceMethod
7676
>
7777
>
7878

79-
type BuildResourceRoutes<O extends ResourceOptions, M extends ResourceMethod> = {
80-
[K in M as GetRouteName<O, K>]: ResourceRoutes[K]
79+
type BuildResourceRoutes<options extends ResourceOptions, method extends ResourceMethod> = {
80+
[methodName in method as GetRouteName<options, methodName>]: ResourceRoutes[methodName]
8181
}
8282

83-
type GetRouteName<O extends ResourceOptions, M extends ResourceMethod> = M extends ResourceMethod
84-
? O extends { names: { [K in M]: infer N extends string } }
85-
? N
86-
: M
83+
type GetRouteName<
84+
options extends ResourceOptions,
85+
method extends ResourceMethod,
86+
> = method extends ResourceMethod
87+
? options extends { names: { [methodName in method]: infer customName extends string } }
88+
? customName
89+
: method
8790
: never
8891

8992
type ResourceRoutes = {
@@ -130,10 +133,10 @@ export type ResourcesOptions = {
130133
* @param base The base route pattern to use for the resources
131134
* @param options Options to configure the resource routes
132135
*/
133-
export function createResources<P extends string, const O extends ResourcesOptions>(
134-
base: P | RoutePattern<P>,
135-
options?: O,
136-
): BuildResourcesMap<P, O> {
136+
export function createResources<base extends string, const options extends ResourcesOptions>(
137+
base: base | RoutePattern<base>,
138+
options?: options,
139+
): BuildResourcesMap<base, options> {
137140
let only = options?.only ?? (ResourcesMethods as readonly ResourcesMethod[])
138141
let param = options?.param ?? 'id'
139142
let indexName = options?.names?.index ?? 'index'
@@ -168,43 +171,48 @@ export function createResources<P extends string, const O extends ResourcesOptio
168171
routes[destroyName] = { method: 'DELETE', pattern: `/:${param}` }
169172
}
170173

171-
return createRoutes(base, routes) as BuildResourcesMap<P, O>
174+
return createRoutes(base, routes) as BuildResourcesMap<base, options>
172175
}
173176

174-
type BuildResourcesMap<B extends string, O extends ResourcesOptions> = BuildRouteMap<
175-
B,
177+
type BuildResourcesMap<base extends string, options extends ResourcesOptions> = BuildRouteMap<
178+
base,
176179
BuildResourcesRoutes<
177-
O,
178-
O extends { only: readonly ResourcesMethod[] } ? O['only'][number] : ResourcesMethod,
179-
GetParam<O>
180+
options,
181+
options extends { only: readonly ResourcesMethod[] }
182+
? options['only'][number]
183+
: ResourcesMethod,
184+
GetParam<options>
180185
>
181186
>
182187

188+
// prettier-ignore
183189
type BuildResourcesRoutes<
184-
O extends ResourcesOptions,
185-
M extends ResourcesMethod,
186-
Param extends string,
190+
options extends ResourcesOptions,
191+
method extends ResourcesMethod,
192+
param extends string,
187193
> = {
188-
[K in M as GetResourcesRouteName<O, K>]: ResourcesRoutes<Param>[K]
194+
[methodName in method as GetResourcesRouteName<options, methodName>]: ResourcesRoutes<param>[methodName]
189195
}
190196

191197
type GetResourcesRouteName<
192-
O extends ResourcesOptions,
193-
M extends ResourcesMethod,
194-
> = M extends ResourcesMethod
195-
? O extends { names: { [K in M]: infer N extends string } }
196-
? N
197-
: M
198+
options extends ResourcesOptions,
199+
method extends ResourcesMethod,
200+
> = method extends ResourcesMethod
201+
? options extends { names: { [methodName in method]: infer customName extends string } }
202+
? customName
203+
: method
198204
: never
199205

200-
type ResourcesRoutes<Param extends string> = {
206+
type ResourcesRoutes<param extends string> = {
201207
index: { method: 'GET'; pattern: `/` }
202208
new: { method: 'GET'; pattern: `/new` }
203-
show: { method: 'GET'; pattern: `/:${Param}` }
209+
show: { method: 'GET'; pattern: `/:${param}` }
204210
create: { method: 'POST'; pattern: `/` }
205-
edit: { method: 'GET'; pattern: `/:${Param}/edit` }
206-
update: { method: 'PUT'; pattern: `/:${Param}` }
207-
destroy: { method: 'DELETE'; pattern: `/:${Param}` }
211+
edit: { method: 'GET'; pattern: `/:${param}/edit` }
212+
update: { method: 'PUT'; pattern: `/:${param}` }
213+
destroy: { method: 'DELETE'; pattern: `/:${param}` }
208214
}
209215

210-
type GetParam<O extends ResourcesOptions> = O extends { param: infer P extends string } ? P : 'id'
216+
// prettier-ignore
217+
type GetParam<options extends ResourcesOptions> =
218+
options extends { param: infer param extends string } ? param : 'id'
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
/**
22
* A helper for working with JSON [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)s.
33
*
4-
* @param body The body of the response.
5-
* @param init (optional) The `ResponseInit` object for the response.
6-
* @returns A `Response` object with a JSON body and the appropriate `Content-Type` header.
4+
* @param data The data to serialize to JSON
5+
* @param init (optional) The `ResponseInit` object for the response
6+
* @returns A `Response` object with a JSON body and the appropriate `Content-Type` header
77
*/
8-
export function json(body: any, init?: ResponseInit): Response {
8+
export function json(data: unknown, init?: ResponseInit): Response {
99
let headers = new Headers(init?.headers)
1010
if (!headers.has('Content-Type')) {
1111
headers.set('Content-Type', 'application/json; charset=UTF-8')
1212
}
1313

14-
return new Response(JSON.stringify(body), { ...init, headers })
14+
return new Response(JSON.stringify(data), { ...init, headers })
1515
}

packages/fetch-router/src/lib/response-helpers/redirect.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/**
22
* A helper for working with redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)s.
33
*
4-
* @param location The location to redirect to.
5-
* @param init (optional) The `ResponseInit` object for the response (or a status code).
6-
* @returns A `Response` object with a redirect header.
4+
* @param location The location to redirect to
5+
* @param init (optional) The `ResponseInit` object for the response, or a status code
6+
* @returns A `Response` object with a redirect header
77
*/
88
export function redirect(location: string | URL, init?: ResponseInit | number): Response {
99
let status = 302

0 commit comments

Comments
 (0)