Что я делаю?
Я хочу сделать отображение объекта поле за полем на объекты некоторого общего типа c. У меня есть 4 случая выполнения (с проверкой времени выполнения и выдачей исключения, если оно не совпадает), которые я использовал в качестве свойств исходного объекта:
false
преобразуется в Smth<unknown> | undefined
true
преобразуется в Smth<unknown>
[]
преобразуется в Smth<unknown>
с 0 или более элементами [true]
преобразуется в Smth<unknown>
с 1 или более элементами
На самом деле это не очень сложно, но я не очень рад, что unknown
в общем c везде. В некоторых случаях я хочу указать, что именно payload
должно иметь этот Smth
(только утверждение во время компиляции, никаких проверок времени выполнения не запланировано). Для этой цели я нашел решение с приведением четырех значений к типам, имеющим дополнительное необязательное поле:
type WithOptPayload<T, P> = T & { payload?: P }
getColDefsByKinds({ b: false as WithOptPayload<false, { u: 99 }> })
type WithOptPayload = T & { payload?: P }
const x = getColDefsByKinds({
a: false,
b: false as WithOptPayload,
c: true,
d: true as WithOptPayload,
e: [],
f: [] as WithOptPayload<[], { w: number }>,
g: [true],
h: [true] as WithOptPayload<[true], any>,
});
type IfExtends = L extends R ? Y : N
interface Smth {
x: string
payload: P
}
type Kind = false | true | readonly [] | readonly [true]
type Unpack = IfExtends>
function getColDefsByKinds(how: T): { [kind in keyof T]: Unpack> } {
return null!
}
And a bit более сложная версия с
type WithOptPayload<T, P> = T extends undefined ? T & { payload?: P } | undefined : T & { payload?: P }
type Kind = false | true | undefined | readonly [] | readonly [true]
type Unpack<F extends Kind, Res> = Exclude<IfExtends<F, undefined, undefined> | IfExtends<F, true, Res> | IfExtends<F, false, Res | undefined> | IfExtends<F, boolean | undefined, never, Res[]>, never>
function getColDefsByKinds<T extends { [key: string]: Kind }>(how: T): { [kind in keyof T]: Unpack<T[kind], Smth<Exclude<T[kind], undefined> extends { payload?: infer P } ? P : unknown>> } {
return null!
}
В чем проблема?
Как видите, в строке
b: false as WithOptPayload<false, { u: 99 }> }
Мне нужно использовать тип false
дважды. Это не длинное слово, но есть другая проблема: машинописный текст считает, что true as false
в порядке . Таким образом, следующие строки компилируются без ошибок и приводят к несоответствию типов времени выполнения и времени компиляции:
x: false as WithOptPayload<true, { u: 99 }>,
y: true as WithOptPayload<false, { u: 99 }>,
z: [true] as WithOptPayload<[], any>,
Мне нужно, чтобы такие преобразования были ошибочными.
Или, может быть, я можно использовать другой способ передачи типа функции сопоставления. Но есть ограничение: все имена свойств должны быть записаны только один раз, поскольку они отличаются от вызова к вызову и не соответствуют никакому интерфейсу.