#javascript #typescript #types
#javascript #typescript #типы
Вопрос:
У меня есть (сгенерированное) определение типа для возможных тел ответа:
type ResponseMap = {
'200': { 'ok': true },
'404': { 'error': true, type: 'NotFound' },
'500': null,
}
Я хочу написать функцию, которая возвращала бы объекты в форме { code, body }
, проверенной на соответствие приведенному выше набору.
Лучшее, чего я смог добиться, это следующее:
type Codes<Bodies> = keyof Bodies
type ResponseCodeBodyPair<Bodies extends Record<string, unknown>> = {
code: Codes<Bodies>,
body: Bodies[Codes<Bodies>]
}
function endpoint(): ResponseCodeBodyPair<ResponseMap> {
if (Math.random()) {
return { code: '200', body: { 'ok': true } }
}
if (Math.random()) {
return { code: '404', body: { 'error': true, type: 'NotFound' } }
}
if (Math.random()) {
return { code: '500', body: null }
}
// This should fail since { ok: true } is not a valid body for 404.
return { code: '404', body: { 'ok': true } }
}
Это проверка отсутствия неизвестных кодов состояния, возвращаемых функцией, и того, что все возвращаемые тела соответствуют одной из возможных форм тела.
Что он не проверяет, так это правильность сопряжения кода с телом, как продемонстрировано последним возвратом.
Как я могу ограничить тип точной формой тела для кода состояния здесь?
Мне нужно что-то вроде
type ResponseCodeBodyPair<Bodies extends Record<string, unknown>> =
Code extends keyof Bodies ? { code: Code, body: Bodies[Code] } : never
… но это, конечно, не работает.
Я боролся с тем же самым много раз и всегда сдавался. Действительно ли это невозможно или я что-то упускаю?
PS Я знаю, что было бы нормально, если бы тип был { code: '200', body: … } | { code: '404', … } | …
, но у меня его нет. Вот почему мне нужно каким-то образом преобразовать карту / запись в этот тип объединения.
Ответ №1:
Я нашел правильный способ сделать это.
Я могу сопоставить { [Code]: Response }
объект с { [Code]: { code: Code, body: Response } }
объектом и принимать значения этого объекта, T[keyof T]
что приводит к точному типу, который мне нужен.
type ResponseCodeBodyPairMap<M> = {
[Code in keyof M]: { code: Code, body: M[Code] }
}
type Values<T> = T[keyof T]
type ResponseCodeBodyPair<M> = Values<ResponseCodeBodyPairMap<M>>
Комментарии:
1. Это было креативно!