Without

Challenge

Implement the type version of Lodash.without, Without<T, U> takes an Array T, number or array U and returns an Array without the elements of U.

type Res = Without<[1, 2], 1>; // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]>; // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]>; // expected to be []

Solution

Zur Lösung dieses Problems können wir wieder variadic tuple types nutzen, um über die Tupel zu laufen:

type Without<T extends any[], U> = T extends [infer H, ...infer R] ...

Die einzige Schwierigkeit ist nun, die Typ variable H mit der Tupel aus den zu entfernenden Elementen zu vergleichen. Um dies zu ermöglichen, definieren wir einfach einen Hilfstypen ArrayToUnion, der aus einem Array / Tupel eine Vereinigung aus Elementen erstellt. Da Vereinigungen in konditionellen Typen distributiv sind, können wir damit den Vergleich durchführen.

// [1,2,3] => 1 | 2 | 3
type ArrayToUnion<T> = T extends [infer H, ...infer R]
  ? H | ArrayToUnion<R>
  : T;

Damit können wir jetzt für jedes Element prüfen, ob es in der Vereinigung enthalten ist, und entsprechend entfernen.

type Without<T extends any[], U> = T extends [infer H, ...infer R]
  ? H extends ArrayToUnion<U>
    ? Without<R, U>
    : [H, ...Without<R, U>]
  : T;

References