Home > Net >  Get deeply nested type in Typescript
Get deeply nested type in Typescript

Time:01-22

I'm trying to create strictly typed function to make calls to my API. My idea was to create type with all needed data like this:

type ApiRoutes = {
  '/user': {
    POST: {
      payload: { mydata: number }
      response: UserData
    }
    DELETE: {
      payload: string
      response: UserData
    }
    GET: {
      payload: string
      response: UserData
    }
  }
}

use it in my function:

const apicall = async <
  E extends keyof ApiRoutes,
  T extends keyof ApiRoutes[E],
  P extends ApiRoutes[E][T]['payload]
>(
  endpoint: E,
  method: T,
  payload: P
):Promise<R extends ApiRoutes[E][T]['response> => {
  //do stuff
}

and then call it like this:

apicall('/user', 'POST', {mydata: 5})
apicall('/user', 'DELETE', 'my text')

My problem is that I get error

Type '"payload"' cannot be used to index type 'ApiRoutes[E][T]'

but when calling apiCall I get correct autocomplete. Do you have any ideas what I have to change? I have a thought that infer keyword might help somehow, but I did not play with it yet so I don't have idea how to approach it.

CodePudding user response:

I'm not entirely sure why this happens... maybe TS is perceiving the method's values as generic objects instead of exact ones (I have to admit it seems like an odd behaviour) but you can fix it by introducing a couple of intermediate types with the infer key as you pointed out:

type Payload<E extends keyof ApiRoutes, T extends keyof ApiRoutes[E]> = ApiRoutes[E][T] extends {payload:infer P} ? P : never;
type Resp<E extends keyof ApiRoutes, T extends keyof ApiRoutes[E]> = ApiRoutes[E][T] extends {response:infer R} ? R : never;

You can now rewrite your function like this:

const apicall = async <
  E extends keyof ApiRoutes,
  T extends keyof ApiRoutes[E],
  P extends Payload<E,T>
>(
  endpoint: E,
  method: T,
  payload: P
):Promise<Resp<E,T>> => {
  //do stuff
}
  • Related