Skip to content

Commit

Permalink
better type examples
Browse files Browse the repository at this point in the history
  • Loading branch information
kwhitley committed Mar 28, 2024
1 parent 4be9d8e commit bb2c739
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 31 deletions.
17 changes: 17 additions & 0 deletions example/types/global-and-route-level-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IRequestStrict } from 'IttyRouter'
import { Router } from 'Router'

const router = Router<IRequestStrict>()

type FooRequest = {
foo: string
} & IRequestStrict

router
.get('/basic', () => new Response('Success!'))
.get('/text', () => 'Success!')
// .get('/params/:foo', ({ foo }) => foo) // should NOT work
.get<FooRequest>('/params/:foo', ({ foo }) => foo) // should work
.get('/json', () => ({ foo: 'bar' }))

export default router
16 changes: 16 additions & 0 deletions example/types/global-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IRequestStrict } from 'IttyRouter'
import { Router } from 'Router'

type FooRequest = {
foo: string
} & IRequestStrict

const router = Router<FooRequest>()

router
.get('/', (request) => {
request.foo // should work
// request.bar // should NOT work
})

export default router
36 changes: 36 additions & 0 deletions example/types/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { IRequestStrict, IRequest, IttyRouter, RequestHandler } from 'IttyRouter'

type UserRequest = {
user: string
} & IRequestStrict

const router = IttyRouter()

const withUser: RequestHandler<UserRequest> = (request) => {
request.user = 'Kevin'
}

router
// upstream request sees the request as IRequest (default), so anything goes
.get('/', (request) => {
request.user = 123 // allowed because IRequest
})

// then we add the middleware defined above as <UserRequest>
.all('*', withUser)

// and now downstream requests expect a UserRequest
.get('/', (request) => {
request.user = 123 // NOT VALID
})

// and if we ever need to restore control, add the generic back in
.get<IRequest>('/', (request) => {
request.user = 123 // now this is ok
})

.get('/', (request) => {
request.user = 123 // and so is this
})

export default router
16 changes: 16 additions & 0 deletions example/types/route-level-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IRequestStrict } from 'IttyRouter'
import { Router } from 'Router'

type FooRequest = {
foo: string
} & IRequestStrict

const router = Router()

router
.get<FooRequest>('/', (request) => {
request.foo // should work
// request.bar // should NOT work
})

export default router
52 changes: 21 additions & 31 deletions src/IttyRouter.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
export type GenericTraps = Record<string, any>

export type RequestLike = {
method: string,
url: string,
method: string
url: string
} & GenericTraps

export type IRequestStrict = {
method: string,
url: string,
route: string,
method: string
url: string
route: string
params: {
[key: string]: string,
},
[key: string]: string
}
query: {
[key: string]: string | string[] | undefined,
},
proxy?: any,
[key: string]: string | string[] | undefined
}
proxy?: any
} & Request

export type IRequest = IRequestStrict & GenericTraps
Expand Down Expand Up @@ -43,32 +43,22 @@ export type Route<R = IRequest, A extends Array<any> = any[]> = <RequestType = R
...handlers: RequestHandler<RequestType, Args>[]
) => IttyRouterType<RequestType, Args>

// this is an alternative UniveralRoute, accepting generics (from upstream), but without
// per-route overrides
// export type UniversalRoute<RequestType extends IRequest = IRequest, Args extends any[] = any[]> = (
// path: string,
// ...handlers: RequestHandler<RequestType, Args>[]
// ) => IttyRouterType<UniversalRoute<RequestType, Args>, Args>

// helper function to detect equality in types (used to detect custom Request on router)
export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;

export type CustomRoutes<R = Route> = {
[key: string]: R,
[key: string]: R
}

export type IttyRouterType<R = IRequest, A extends any[] = any[], Output = any> = {
__proto__: IttyRouterType<R>,
routes: RouteEntry[],
__proto__: IttyRouterType<R>
routes: RouteEntry[]
fetch: <Args extends any[] = A>(request: RequestLike, ...extra: Args) => Promise<Output>
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>,
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>>

export const IttyRouter = <
Expand Down

0 comments on commit bb2c739

Please sign in to comment.