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 extends Resource, P extends Exclude[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 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' })