Предположим, что у нас есть такие определения:
interface BoxType<T> { box: T }
interface FooType { foo: string }
interface BarType { bar: string }
declare function BoxType<T>(): BoxType<T>;
мы можем дать функции convert()
следующую сигнатуру вызова:
declare function convert<T extends object>(
input: { [K in keyof T]: BoxType<T[K]> }
): BoxType<{ [K in keyof T]-?: { name: K, value: T[K] } }[keyof T]>;
который, я думаю, ведет себя так, как вы хотите:
const result = convert({
foo: BoxType<FooType>(),
bar: BoxType<BarType>()
})
/*
const result: BoxType<{
name: "foo";
value: FooType;
} | {
name: "bar";
value: BarType;
}>
*/
Функция convert()
является универсальной для T
, тип объекта никогда напрямую не используется как вход или выход, но выглядит как тип input
с "распакованными" свойствами. Мы можем определить тип input
в терминах T
как простой отображаемый тип. {[K in keyof T]: BoxType<T[K]> }
, где мы "упаковываем" каждое свойство. Поскольку тип input
сопоставляется с keyof T
, он считается «гомоморфным» отображаемым типом, и компилятор может выводить T
из input
. Пока это то же самое, что и у вас.
Для возвращаемого типа функции мы хотим взять каждый ключ свойства K
в keyof T
и создать объединение { name: K, value: T[K] }
. В вашем коде вы сначала взяли союз перед отображением. Но тип {name: keyof T, value: T[keyof T]}
допускает всевозможные нежелательные значения, где name
происходит от одного ключа, а value
— от другого. Чтобы разделить их, нам нужно выполнить итерацию по каждому ключу K
в keyof T
, и один из простых способов сделать это — использовать другой сопоставленный тип, например {[K in keyof T]: {name: K, value: T[K]}}
.
.
Конечно, этот тип выглядит как {foo: {name: "foo", value: FooType}, bar: {name: "bar", value: BarType}}
... мы хотим получить объединение типов значений свойств, и нам вообще не нужны ключи. К счастью, здесь мы можем просто проиндексировать его с помощью keyof T
, чтобы создать объединение типов значений свойств. Отсюда {[K in keyof T]: {name: K, value: T[K]}}[keyof T]
.
И, наконец, мы заключаем это в BoxType
: BoxType<{ [K in keyof T]: { name: K, value: T[K] }}[keyof T]>
. Все сделано! Подождите, упс, единственная разница между этим и приведенным выше типом вывода заключается в том, что мы модифицируем свойства сопоставленного типа, убедившись, что они не являются необязательными: BoxType<{ [K in keyof T]-?: { name: K, value: T[K] } }[keyof T]>
in, если ваш тип ввода T
имеет необязательные свойства. И вот мы закончили.
Ссылка на код игровой площадки
Что такое
BoxType
? Это тип или функция? Что такоеMyType
? Свойство называетсяname
илиtype
? Пожалуйста, подумайте о том, чтобы сделать этот код минимальным воспроизводимым примером, подходящим для добавления в автономную IDE, желательно, чтобы вы сами протестировали его, чтобы убедиться, что вы спрашиваете то, что собираетесь спрашивать.Соответствует ли это вашим потребностям? Если это так, я напишу ответ, объясняющий это. Если нет, отредактируйте вопрос, чтобы уточнить, что вы ищете. Удачи!
@jcalz это именно то, что мне нужно, спасибо
@jcalz
MyType
должен был бытьBoxType
, извините за путаницу. Мне интересно увидеть ваше объяснение. еще раз спасибо