Integers Comparator

Challenge

Implement a type-level integers comparator. We’ve provided an enum for indicating the comparison result, like this:

  • If a is greater than b, type should be Comparison.Greater.
  • If a and b are equal, type should be Comparison.Equal.
  • If a is lower than b, type should be Comparison.Lower.

Note that a and b can be positive integers or negative integers or zero, even one is positive while another one is negative.

Solution

Zur Loesung dieses Problems definieren wir wieder einige Hilfstypen. Beginnen wir mit IsNegative, welcher bei einer negativen Zahl das Vorzeichen entfernt.

// -5 => 5
//  5 => 5
type NegativeToPositive<A extends number> =
  `${A}` extends `-${infer N extends number}` ? N : A;

Nun brauchen wir einen Hilfstyp, der uns das Ergebnis eines Vergleiches von zwei Zahlen liefert. Die Idee ist hierbei, dass man einen Array so lange mit Zahlen füllt, bis dieser entweder Argument A, oder Argument B entspricht. Je nachdem, welcher Wert zuerst erreicht wird, wird eine Aussage über deren Verhältnis getroffen. Zusätzlich wird, sobald der Akkumulator A oder B entspricht, noch geprüft, ob vielleicht auch sogar der zweite Eingabeparameter erreicht ist => beide sind gleich.

type CompareNumbers<
  A extends number,
  B extends number,
  T extends any[] = []
> = T["length"] extends A
  ? T["length"] extends B
    ? Comparison.Equal
    : Comparison.Lower
  : T["length"] extends B
  ? Comparison.Greater
  : CompareNumbers<A, B, [...T, 1]>;

Hiermit sind wir schon fast am Ziel, man muss nun lediglich noch mit den negativen Zahlen richtig hantieren: 1.) Ist A negativ und die B nicht => B > A 2.) Ist A positiv und B positiv => A > B 3.) Sind A und B negativ, so nutzt man den Hilfstypen CompareNumbers, um die beiden zu vergleichen.

type Comparator<
  A extends number,
  B extends number
> = A extends NegativeToPositive<A>
  ? B extends NegativeToPositive<B>
    ? CompareNumbers<A, B>
    : Comparison.Greater
  : B extends NegativeToPositive<B>
  ? Comparison.Lower
  : CompareNumbers<NegativeToPositive<B>, NegativeToPositive<A>>;

Kombiniert man alles, so ergibt sich folgende Lösung:

enum Comparison {
  Greater,
  Equal,
  Lower,
}

type NegativeToPositive<A extends number> =
  `${A}` extends `-${infer N extends number}` ? N : A;

type CompareNumbers<
  A extends number,
  B extends number,
  T extends any[] = []
> = T["length"] extends A
  ? T["length"] extends B
    ? Comparison.Equal
    : Comparison.Lower
  : T["length"] extends B
  ? Comparison.Greater
  : CompareNumbers<A, B, [...T, 1]>;

type Comparator<
  A extends number,
  B extends number
> = A extends NegativeToPositive<A>
  ? B extends NegativeToPositive<B>
    ? CompareNumbers<A, B>
    : Comparison.Greater
  : B extends NegativeToPositive<B>
  ? Comparison.Lower
  : CompareNumbers<NegativeToPositive<B>, NegativeToPositive<A>>;

References