Required Keys

Challenge

Implement the advanced util type RequiredKeys<T>, which picks all the required keys into a union.

For example

type Result = RequiredKeys<{ foo: number; bar?: string }>;
// expected to be “foo”

Solution

Der Trick bei dieser Herausforderung ist es, die Attribute zu finden, die nicht benötigt werden. Hierzu kann man bei der Erstellung des Mapped Typs prüfen, ob das Entfernen eines Attributes immer noch dem gleichen Typ wie davor entspricht:

type TExample = { foo: number; bar?: string };

//Ergibt false, da { bar? : string} nicht mit TExample uebereinstimmt
type TResult = Omit<TExample, "foo"> extends TExample ? true : false;

//Ergibt true, da { foo : string} mit TExample uebereinstimmt. Dies liegt daran, dass 'bar' optional ist, d.h. wenn es wie in diesem Fall fehlt,
//die Bedingung trotzden erfuellt bleibt.
type TResult = Omit<TExample, "bar"> extends TExample ? true : false;

Somit kann man einfach prüfen, ob nach dem Entfernen eines Attributes noch weiterhin die Typen übereinstimmen und man erhält somit alle Attribute, die ‘Pflicht’ sind.

//Hierdurch erhaelt man z.B. aus { foo: number; bar?: string } => {foo: number}
type GetRequired<T> = {
  [Key in keyof T as Omit<T, Key> extends T ? never : Key]: T[Key];
};

Um nun noch alle verpflichtenden Attribute als Vereinigung zu erhalten, muss man lediglich den keyof-Operator nutzen.

type GetRequired<T> = keyof {
  [Key in keyof T as Omit<T, Key> extends T ? never : Key]: T[Key];
};

References

Utility Types - Mapped Types