Append to object

Challenge

Implement a type that adds a new field to the interface. The type takes the three arguments. The output should be an object with the new field.

For example

type Test = { id: "1" };
type Result = AppendToObject<Test, "value", 4>; // expected to be { id: '1', value: 4 }

Solution

Zur Lösung dieses Problems können wir eine sogenannte Intersection (Schnittmenge) zwischen zwei Objekt-Typen nutzen, um ein neues Objekt zu erzeugen. Generell kommen Intersections bei der Erweiterung/ Veränderung von Objekten oft zum Einsatz.

type AppendToObject<T, U, V> = T & { [P in U]: V };

Das Problem ist allerdings, dass dies nicht richtig funktioniert, da falls U bereits in T vorhanden ist, eine Intersection nicht möglich ist:

{id: "1"} & {id:"4"} // never
{id: "1"} & { value: 4} // {id: '1', value: 4}

Wir benötigen also eine andere Lösung. Eine Möglichkeit z.B. ist es nun, einen Mapped Type zu erstellen, bei dem über eine Vereinigung der Eigenschaft aus T und U iteriert wird. Dies funktioniert, man muss allerdings für jedes Attribut prüfen, ob auch das Attribut in T existiert, da sonst kein Zugriff auf den Wert der Eigenschaft möglich ist.

type AppendToObject<T, U extends string, V> = {
  [Key in keyof T | U]: Key extends keyof T ? T[Key] : V;
};

References