MHebes
MHebes

Reputation: 3197

Nested mapped types in TypeScript

I have this type:

type Requests = {
  "/endpoint": {
    POST: {
      body: any
      response: any
    }
    // ...
  }
  // ...
}

I want to map response to another value, but I get an error in vscode:

export type API = {
  [route in keyof Requests]: {
    [method in keyof Requests[route]]: {
      body: Requests[route][method]["body"] // 🚨 Type '"body"' cannot be used to index type 'Requests[route][method]'
      response: Requests[route][method]["response"] | { error: any } // 🚨 Type '"request"' cannot be used to index type 'Requests[route][method]'
    }
  }
}

Is there any way to accomplish this?

Upvotes: 4

Views: 660

Answers (2)

Alex Wayne
Alex Wayne

Reputation: 186984

It appears typescript can't really generalize about any key that might be in that type, even though the list is finite and known. But it's easy to help it out.

If you specify the general format for the type:

type RequestsFormat = Record<string, { [key: string]: { body: any, response: any } }>

And then extend that:

interface Requests extends RequestsFormat {
  "/endpoint": {
    POST: {
      body: any
      response: any
    }
  }
}

Then typescript can figure out the rest.

Playground

Upvotes: -1

Countingstuff
Countingstuff

Reputation: 783

This should do what you want

type Requests = {
    "/endpoint": {
    POST: {
        body: string
        response: number
    }
    // ...
    },

}


export type API = {
    [route in keyof Requests]: {
        [method in keyof Requests[route]]: {
        body: Requests[route][method] extends { body: infer R } ? R : never
        response: (Requests[route][method] extends { response: infer R } ? R : never) | { error: any }
        }
    }
}

Upvotes: 4

Related Questions