преобразовать набор параметризованных универсальных типов в один универсальный тип, параметризованный с дизъюнкцией параметров из исходного набора универсальных типов

avatar
Alexander
9 августа 2021 в 01:38
74
1
1

как определить функцию covert из приведенного ниже примера?

const result: BoxType<{
  name: "foo",
  value: FooType
} | {
  name: "bar",
  value: BarType
}> = /* --> */ convert<{
  foo: FooType,
  bar: BarType
}>({
  foo: BoxType<FooType>(),
  bar: BoxType<BarType>()
}) /* <-- */

я мог бы придумать что-то вроде этого

type Input<T> = {[type in keyof T]: BoxType<T[type]>}
type Output<T> = BoxType<{type: keyof T, value: T[keyof T]}>

function convert<T>(input: Input<T>): Output<T> {
  /* ... */
}

но тип Output явно недействителен. это вообще осуществимо? :)

Источник
jcalz
9 августа 2021 в 02:32
0

Что такое BoxType? Это тип или функция? Что такое MyType? Свойство называется name или type? Пожалуйста, подумайте о том, чтобы сделать этот код минимальным воспроизводимым примером, подходящим для добавления в автономную IDE, желательно, чтобы вы сами протестировали его, чтобы убедиться, что вы спрашиваете то, что собираетесь спрашивать.

jcalz
9 августа 2021 в 02:35
1

Соответствует ли это вашим потребностям? Если это так, я напишу ответ, объясняющий это. Если нет, отредактируйте вопрос, чтобы уточнить, что вы ищете. Удачи!

Alexander
9 августа 2021 в 02:54
0

@jcalz это именно то, что мне нужно, спасибо

Alexander
10 августа 2021 в 01:32
0

@jcalz MyType должен был быть BoxType, извините за путаницу. Мне интересно увидеть ваше объяснение. еще раз спасибо

Ответы (1)

avatar
jcalz
10 августа 2021 в 02:30
1

Предположим, что у нас есть такие определения:

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 имеет необязательные свойства. И вот мы закончили.

Ссылка на код игровой площадки