MapTypes

Challenge

Implement MapTypes<T, R> which will transform types in object T to different types defined by type R which has the following structure

type StringToNumber = {
  mapFrom: string; // value of key which value is string
  mapTo: number; // will be transformed for number
};

Examples:

type StringToNumber = { mapFrom: string; mapTo: number };
MapTypes<{ iWillBeANumberOneDay: string }, StringToNumber>; // gives { iWillBeANumberOneDay: number; }

Be aware that user can provide a union of types:

type StringToNumber = { mapFrom: string; mapTo: number };
type StringToDate = { mapFrom: string; mapTo: Date };
MapTypes<{ iWillBeNumberOrDate: string }, StringToDate | StringToNumber>; // gives { iWillBeNumberOrDate: number | Date; }

If the type doesn’t exist in our map, leave it as it was:

type StringToNumber = { mapFrom: string; mapTo: number };
MapTypes<
  { iWillBeANumberOneDay: string; iWillStayTheSame: Function },
  StringToNumber
>; // // gives { iWillBeANumberOneDay: number, iWillStayTheSame: Function }

Solution

Zur Lösung dieses Problems erstellen wir einen mapped type. Zuerst definieren wir hierfür die Bedingungen für die generischen Argumente. Für R erwarten wir hier ein Objekttyp, der zumindest über die beiden Eigenschaften mapFrom sowie mapTo verfügt:

type MapTypes<
  T extends Record<string, unknown>,
  R extends { mapFrom: any; mapTo: any }
>

Nun erzeugen wir einen mapped type, indem wir über die Eigenschaften des Objektes iterieren, und ersetzen den Wert zu der jeweiligen Eigenschaft mit dem neuen Wert mapTo.

type MapTypes<
  T extends Record<string, unknown>,
  R extends { mapFrom: any; mapTo: any }
> = {
  [Key in keyof T]: T[Key] extends R["mapFrom"]
    ? R extends { mapFrom: T[Key]; mapTo: any }
      ? R["mapTo"]
      : never
    : T[Key];
};

References