ObjectEntries
Challenge
Implement the type version of Object.entries
For example
interface Model {
name: string;
age: number;
locations: string[] | null;
}
type modelEntries = ObjectEntries<Model>; // ['name', string] | ['age', number] | ['locations', string[] | null];
Solution
Auch zur Lösung dieses Problems erstellen wir wieder einen Mapped-Type, bei dem wir mittels des -?-Operator das Vorhandensein erzwingen
Weiterhin müssen wir noch einen Hilfstypen definieren, über den wir undefined aus einer Vereinigung entfernen. Anschließend erhalten wir über keyof T am Ende des Mapped Types alle Werte (in dem Fall die Tupel aus Eigenschaft und Wert) als Vereinigung, und haben somit das Ergebnis.
type RemoveUndefined<T> = [T] extends [undefined] ? T : Exclude<T, undefined>;
type ObjectEntries<T> = {
[K in keyof T]-?: [K, RemoveUndefined<T[K]>];
}[keyof T];
Eine weitere Lösung besteht darin, rekursiv den Typen aufzurufen, und dabei eine Vereinigung zu erstellen:
type RemoveUndefined<T> = [T] extends [undefined] ? T : Exclude<T, undefined>;
// Vereinigungen in konditionellen Typen sind distributiv, daher koennen wir hierdurch ueber jeden Wert laufen und eine finale Vereingung aus [Eigenschaft, Wert] erstellen
type RecordToUnion<T extends Record<string, any>, U = keyof T> = U extends U
? U extends keyof T
? [U, RemoveUndefined<T[U]>] | RecordToUnion<T, Exclude<T, U>>
: []
: never;
// Da als Base-Case ein leerer Array definiert wurde, muessen wir diesen noch aus der Vereinigung entfernen
type ObjectEntries<T extends Record<string, any>> = Exclude<
RecordToUnion<T>,
[]
>;