Skip to content

Commit

Permalink
fixing types (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwhitley committed Mar 29, 2024
1 parent 25345f4 commit 94ce410
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 34 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Changelog

- **v5.0.3**
- fixed: (TypeScript) middleware corrupting downstream request types and args
- **v5.0.2**
- fixed: AutoRouter was missing the router-level generics support of the other 2 routers.
- fixed: All 3 routers had their 3rd generic argument, ResponseType added per the spec.
Expand Down
56 changes: 56 additions & 0 deletions examples/types/additional-arguments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { RequestHandler } from '../../src/types'
import { IRequestStrict } from '../../src/types'
import { Router } from '../../src/Router'
import { IttyRouter } from '../../src/IttyRouter'
import { AutoRouter } from '../../src/AutoRouter'
import { IRequest } from 'itty-router'

// we define our environment
type Environment = { age: number }
type Pet = { name: string }

// and now both args combined (that Workers send to the .fetch())
type Args = [Environment]
type AlternativeArgs = [Pet]

// creating some middleware that needs access to CF variables
export const withUser: RequestHandler<IRequest, Args> =
(request, env) => {
request.user = 'Kevin'
env.age = 123
env.name = 'Kevin' // invalid
}

const router = Router<IRequestStrict, Args>()

router
// before middleware
.get('/', (request, env) => {
request.user = 'kevin' // invalid (strict)
env.whatever = 123 // valid (any)
env.age = 123 // valid (any)
})

// route-level overrides
.get<IRequest, Args>('/', (request, env) => {
request.foo = 'bar' // invalid
env.whatever = 123 // invalid
env.age = 123 // valid
})
// route-level overrides
.get<IRequest, AlternativeArgs>('/', (request, env) => {
request.foo = 'bar' // invalid
env.age = 123 // invalid
env.name = 'Mittens' // valid
})

// after middleware
.get('/', withUser, (request, env) => {
request.user = 'Kevin'
env.age = 123 // valid
env.whatever = 123 // invalid
})

.get('/', (request, env) => {
env.age = 'foo' // valid (any)
})
14 changes: 11 additions & 3 deletions examples/types/global-and-route-level-request.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { IRequestStrict } from 'IttyRouter'
import { Router } from 'Router'
import { IRequestStrict } from '../../src/types'
import { Router } from '../../src/Router'

const router = Router<IRequestStrict>()
type Pet = { name: string }
type List = number[]

const router = Router<IRequestStrict, [Pet]>()

type FooRequest = {
foo: string
Expand All @@ -10,6 +13,11 @@ type FooRequest = {
router
.get('/basic', () => new Response('Success!'))
.get('/text', () => 'Success!')
.get('/text', (request, env) => {
env.name = 'foo'
env.bar = 'baz' // invalid
})
.get('/', (r, env) => env.foo )
// .get('/params/:foo', ({ foo }) => foo) // should NOT work
.get<FooRequest>('/params/:foo', ({ foo }) => foo) // should work
.get('/json', () => ({ foo: 'bar' }))
Expand Down
28 changes: 12 additions & 16 deletions examples/types/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
import { IRequestStrict, IRequest, RequestHandler } from '../../src/types'
import { IttyRouter } from 'IttyRouter'
import { IttyRouter } from '../../src/IttyRouter'

type UserRequest = {
user: string
} & IRequestStrict

// we define a *strict* router for this demo
const router = IttyRouter<IRequestStrict>()

const withUser: RequestHandler = (request) => {
// middleware with explicit request-type (generic)
const withUser: RequestHandler<UserRequest> = (request) => {
request.user = 'Kevin'
}

router
// upstream request sees the request as IRequest (default), so anything goes
// request will be IRequestStrict here, thus no user property
.get('/', (request) => {
request.user = 123 // not OK
request.user = 'Kevin' // invalid
})

// then we add the middleware defined above as <UserRequest>
// then we add the middleware defined above, allowing the handler chain to inherit the request type
.all('*', withUser, (request) => {
request.user = 'Kevin'
request.user = 123 // NOT ok
})

// and if we ever need to restore control, add the generic back in
.get<UserRequest, []>('/', (request) => {
request.user = 'Kevin'
request.user = 123 // NOT ok
})

// request will be back to IRequestStrict here, thus no user property
.get('/', (request) => {
request.user = 'Kevin' // still ok
request.user = 123 // NOT ok
request.user = 'Kevin' // invalid
})

router.get('/', (request) => {
request.user = 123 // NOT ok
// and if we ever need to restore control, add the generic back in
.get<UserRequest>('/', (request) => {
request.user = 'Kevin'
})

export default router
10 changes: 6 additions & 4 deletions src/IttyRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
export const IttyRouter = <
RequestType extends IRequest = IRequest,
Args extends any[] = any[],
ResponseType = any
>({ base = '', routes = [], ...other }: IttyRouterOptions = {}): IttyRouterType<RequestType, Args, ResponseType> =>
ResponseType = any,
GlobalRequestType = RequestType,
>({ base = '', routes = [], ...other }: IttyRouterOptions = {}): IttyRouterType<RequestType, Args, ResponseType, GlobalRequestType> =>
// @ts-ignore
({
__proto__: new Proxy({}, {
// @ts-expect-error (we're adding an expected prop "path" to the get)
get: (target: any, prop: string, receiver: object, path: string) =>
(route: string, ...handlers: RequestHandler<RequestType, Args>[]) =>
(route: string, ...handlers: RequestHandler<GlobalRequestType, Args>[]) =>
routes.push(
[
prop.toUpperCase(),
Expand Down Expand Up @@ -53,4 +55,4 @@ export const IttyRouter = <
if ((response = await handler(request.proxy ?? request, ...args)) != null) return response
}
},
} as IttyRouterType<RequestType, Args>)
})
21 changes: 11 additions & 10 deletions src/types/IttyRouterType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ import { CustomRoutes } from './CustomRoutes'
export type IttyRouterType<
R = IRequest,
A extends any[] = any[],
ResponseType = any
ResponseType = any,
GlobalRequestType = R,
> = {
__proto__: IttyRouterType<R>
routes: RouteEntry[]
fetch: <Args extends any[] = A>(request: RequestLike, ...extra: Args) => Promise<ResponseType>
all: Route<R, A>
delete: Route<R, A>
get: Route<R, A>
head: Route<R, A>
options: Route<R, A>
patch: Route<R, A>
post: Route<R, A>
put: Route<R, A>
} & CustomRoutes<Route<R, A>> & GenericTraps
all: Route<GlobalRequestType, A>
delete: Route<GlobalRequestType, A>
get: Route<GlobalRequestType, A>
head: Route<GlobalRequestType, A>
options: Route<GlobalRequestType, A>
patch: Route<GlobalRequestType, A>
post: Route<GlobalRequestType, A>
put: Route<GlobalRequestType, A>
} & CustomRoutes<Route<GlobalRequestType, A>> & GenericTraps
2 changes: 1 addition & 1 deletion src/types/Route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ export type Route<
>(
path: string,
...handlers: RequestHandler<RequestType, Args>[]
) => IttyRouterType<RequestType, Args>
) => IttyRouterType<R, A>

0 comments on commit 94ce410

Please sign in to comment.