Привести объект к другому по сопоставленному свойству в значениях - PullRequest
0 голосов
/ 22 мая 2019

Мне нужно преобразовать один объект в другой в соответствии с некоторыми правилами.

  • Есть Types enum;
  • Есть два интерфейса Color и Number;
  • Существует интерфейс ValuesByType, где перечисление Types сопоставлено с интерфейсами Color и Number;
  • Three - это source объект (словарь / запись) со свойством type, равным Types enum.

Мне нужно написать типы для функции convert, которая возвращает новый объект с теми же ключами, что и у объекта source, но значения должны ссылаться на интерфейсы, которые отображаются на ValuesByType.

Пример: * * тысяча двадцать-восемь

interface Color {}
interface Number {}

enum Types {
  Color,
  Number,
}

interface ValuesByType {
  [Types.Color]: Color,
  [Types.Number]: Number,
}

interface SourceValue<T extends Types = Types> {
  type: T;
}


// Need to define types for this function
function convert(source: SourceValue) {
  ...
}

const source: SourceValue = {
  foo: {
    type: Types.Color,
  },
  bar: {
    type: Types.Number,
  },
};

const result = convert(source);

// The type of "result" veriable should be:
// {
//     foo: Color,
//     bar: Number
// }

Я пробовал этот способ, но он не работает:

export type Convert = <
  T extends Types,
  S extends Record<keyof S, SourceValue<T>>
>(
  source: S,
) => Record<keyof S, ValuesByType[T]>;


1 Ответ

1 голос
/ 22 мая 2019

Одна проблема, которую вы хотите решить при решении этой проблемы, это тип входного объекта _sources. На вашей ссылке TS Playground она в настоящее время набирается как:

const _source: { 
  foo: { type: Types; }; 
  bar: { type: Types; };
};

Обратите внимание, что параметры type устанавливаются на Types вместо конкретного Types.Color или Types.Number.

В Typescript 3.4 вы можете исправить это, добавив as const после определения:

const _source = {
  foo: { type: Types.Color },
  bar: { type: Types.Number },
} as const;

или, до версии 3.4, вы можете использовать Types.Color as Types.Color, чтобы получить машинописный текст для обработки их как литеральных значений.

После того, как это будет очищено, Mapped Type получит необходимое вам возвращаемое значение. Я написал это как универсальный вспомогательный тип.

type ConvertedSources<T extends Record<string, SourceValue<any>>> = {
  [key in keyof T]: ValuesByType[T[key]['type']]
};

Если вы передадите typeof _source этому, вы увидите, как каждое свойство в _source будет сопоставлено с соответствующим типом значения на основе вашего интерфейса ValuesByType.

Затем вам просто нужно заставить свою функцию использовать это в своей сигнатуре, сохраняя то же ограничение типа:

function convert<
  TSources extends Record<string, SourceValue<any>>
  >(source: TSources): ConvertedSources<TSources> { ... }

Обратите внимание, что вам также необходимо привести результат вашего reduce() вызова к ConvertedSources<TSources> или изменить часть внутреннего кода, чтобы сохранить типы.

TS Playground Demo

...