Вложенные сопоставленные типы в TypeScript

#typescript

#typescript

Вопрос:

У меня есть такой типаж:

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

Я хочу сопоставить response с другим значением, но я получаю сообщение об ошибке в 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]'
    }
  }
}
  

Есть ли какой-либо способ добиться этого?

Ответ №1:

Это должно делать то, что вы хотите

 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 }
        }
    }
}
  

Комментарии:

1. Вау, вообще не знал о infer ключевом слове. Спасибо за это, это должно быть полезно для многих подобных проблем.

2.В таком случае позвольте мне упомянуть, что существует распространенный трюк с использованием ключевого слова infer , который нигде не задокументирован. Вывод гораздо более эффективен в контексте функций, поэтому часто со сложными типами помогает добавить вспомогательный тип функции, так что, например, если я хочу получить тип хвоста кортежа, я могу сделать так. type S<T extends any[]> = ((...args: T) => any) extends ((s: any, ...rest: infer R) => any) ? R : [] type W = S<[1, "it", "gets", "the", "rest"]>

Ответ №2:

Похоже, что typescript не может действительно обобщать какой-либо ключ, который может быть в этом типе, даже если список конечен и известен. Но с этим легко справиться.

Если вы укажете общий формат для типа:

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

А затем расширьте это:

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

Затем typescript может разобраться с остальным.

Игровая площадка

Комментарии:

1. Это исправляет ошибку, но расширяет keyof тип для Requests to string . например, тип Requests["asdf"] не будет выдавать ошибку из-за Record<string, ...> типа.