Skip to content

Instantly share code, notes, and snippets.

@hanachin
Last active June 22, 2020 17:21
Show Gist options
  • Save hanachin/fb027bd7c0848d03eb89fbfe6862e581 to your computer and use it in GitHub Desktop.
Save hanachin/fb027bd7c0848d03eb89fbfe6862e581 to your computer and use it in GitHub Desktop.
type PathConverter<P> = (params: P) => string
type routes = {
boards: {
GET: {
pathParams: {}
bodyParams: {}
},
POST: {
pathParams: {}
bodyParams: { title: string }
}
},
board: {
GET: {
pathParams: { id: number }
bodyParams: {}
},
PATCH: {
pathParams: { id: number }
bodyParams: { title: string }
}
}
}
type HttpVerbs = 'GET' | 'POST' | 'PATCH'
type Verbs<R> = keyof R & HttpVerbs
type Resources = keyof routes
function f<
V extends Verbs<routes[R]>,
R extends Resources,
P extends routes[R][V] extends { pathParams: infer PP, bodyParams: infer BP } ? PP & BP : {}
>(verb: V, name: R, params: P) {
return verb + name + JSON.stringify(params)
}
const updateBoard = ['PATCH', 'board']
f('PATCH', 'board', { id: 1, title: 'hi' })
type HttpVerbs = 'GET' | 'POST' | 'PATCH' | 'DELETE'
const board = () => '/boards'
const id = ({ id }: { id: number }) => id.toString()
// const id = ({ id, fuga }: { id: number, fuga: number }) => id.toString() + fuga.toString()
const boards = { pathSpec: [board, id] as const } as {
Methods?: {
PATCH: { id: number; title: string },
DELETE: { id: number }
}
}
type Resource = {
Methods?: { [verb in HttpVerbs]?: any }
pathSpec?: ((args: any) => string)[]
}
function f<
M extends keyof Exclude<T['Methods'], undefined>,
T extends Resource,
P extends Exclude<T['Methods'], undefined>[M]
>(method: M, { pathSpec }: T, params: P): string {
const paths = pathSpec.map(f => f(params))
return method + paths.join('/')
}
type P = Exclude<(typeof boards)['Methods'], undefined>['DELETE']
// const params = { id: 1, title: 'hi' }
const params = { id: 1 }
type AP = typeof params
type XP = Exclude<keyof AP, keyof P> extends never ? P : never
f('PATCH', boards, { id: 1, title: 'hi' })
f('PATCH', boards, { title: 'hi' })
f('DELETE', boards, { id: 1 })
f('DELETE', boards, { id: 1, title: 'hi' })
f('GET', boards, { id: 1 })
f('FUGA', boards, { id: 1, title: 'hi' })
type HttpVerbs = 'GET' | 'POST' | 'PATCH' | 'DELETE'
const boards = { path: ({ id }: { id: number }) => `/boards/${id}` } as {
path?: ({ id: number}) => string
Methods?: {
PATCH: {
names: { id: number }
params: { title: string }
}
DELETE: {
names: { id: number }
}
}
}
type Resource = {
path?: (names: any, opts?: any) => string
Methods?: { [verb in HttpVerbs]?: any }
}
function f<
M extends keyof Exclude<T['Methods'], undefined>,
T extends Resource,
P extends Exclude<T['Methods'], undefined>[M]
>(method: M, { path }: T, params: P): string {
return method + path(params)
}
type P = Exclude<(typeof boards)['Methods'], undefined>['DELETE']
// const params = { id: 1, title: 'hi' }
const params = { id: 1 }
type AP = typeof params
type XP = Exclude<keyof AP, keyof P> extends never ? P : never
f('PATCH', boards, { names: { id: 1 }, params: { title: 'hi' } })
f('PATCH', boards, { params: { title: 'hi' } })
f('DELETE', boards, { names: { id: 1 } })
f('DELETE', boards, { names: { id: 1 }, params: { title: 'hi' } })
f('GET', boards, { names: { id: 1 } })
f('FUGA', boards, { names: { id: 1 }, params: { title: 'hi' } })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment