Straightforward recursive number-to-words algorithm in TypeScript

This is a recursive number-to-words algorithm in TypeScript. It tries to be straightforward and just uses if statements to make the logic a bit easier to follow.

The key ideas are:

function numberToWords(num: number): string {
  if (num < 0) {
    return `minus ${numberToWords(-num)}`;
  }
  if (num in DIRECT) {
    return DIRECT[num];
  }
  if (num < 100) {
    const units = num % 10;
    const tens = num - units;
    return `${DIRECT[tens]} ${numberToWords(units)}`;
  }
  if (num < 1000) {
    const sub_hundreds = num % 100;
    const hundreds = (num - sub_hundreds) / 100;
    let words = `${numberToWords(hundreds)} hundred`;
    if (sub_hundreds) {
      words += ` and ${numberToWords(sub_hundreds)}`;
    }
    return words;
  }
  if (num < 1000000) {
    const sub_thous = num % 1000;
    const thousands = (num - sub_thous) / 1000;
    let words = `${numberToWords(thousands)} thousand`;
    if (sub_thous) {
      if (sub_thous < 100) {
        words += " and";
      }
      words += ` ${numberToWords(sub_thous)}`;
    }
    return words;
  }
  if (num < 1000000000) {
    const sub_mills = num % 1000000;
    const millions = (num - sub_mills) / 1000000;
    let words = `${numberToWords(millions)} million`;
    if (sub_mills) {
      if (sub_mills < 100) {
        words += " and";
      }
      words += ` ${numberToWords(sub_mills)}`;
    }
    return words;
  }
  throw new Error("I can't count that high sorry");
}

Jest tests:

describe(numberToWords.name, () => {
  test("convert number to English words", () => {
    expect(numberToWords(-1)).toBe("minus one");
    expect(numberToWords(0)).toBe("zero");
    expect(numberToWords(1)).toBe("one");
    expect(numberToWords(2)).toBe("two");
    expect(numberToWords(3)).toBe("three");
    expect(numberToWords(4)).toBe("four");
    expect(numberToWords(5)).toBe("five");
    expect(numberToWords(6)).toBe("six");
    expect(numberToWords(7)).toBe("seven");
    expect(numberToWords(8)).toBe("eight");
    expect(numberToWords(9)).toBe("nine");
    expect(numberToWords(10)).toBe("ten");
    expect(numberToWords(11)).toBe("eleven");
    expect(numberToWords(12)).toBe("twelve");
    expect(numberToWords(13)).toBe("thirteen");
    expect(numberToWords(14)).toBe("fourteen");
    expect(numberToWords(15)).toBe("fifteen");
    expect(numberToWords(16)).toBe("sixteen");
    expect(numberToWords(17)).toBe("seventeen");
    expect(numberToWords(18)).toBe("eighteen");
    expect(numberToWords(19)).toBe("nineteen");

    expect(numberToWords(20)).toBe("twenty");
    expect(numberToWords(30)).toBe("thirty");
    expect(numberToWords(40)).toBe("forty");
    expect(numberToWords(50)).toBe("fifty");
    expect(numberToWords(60)).toBe("sixty");
    expect(numberToWords(70)).toBe("seventy");
    expect(numberToWords(80)).toBe("eighty");
    expect(numberToWords(90)).toBe("ninety");

    expect(numberToWords(23)).toBe("twenty three");
    expect(numberToWords(42)).toBe("forty two");
    expect(numberToWords(88)).toBe("eighty eight");
    expect(numberToWords(99)).toBe("ninety nine");

    expect(numberToWords(100)).toBe("one hundred");
    expect(numberToWords(200)).toBe("two hundred");
    expect(numberToWords(300)).toBe("three hundred");
    expect(numberToWords(400)).toBe("four hundred");
    expect(numberToWords(500)).toBe("five hundred");
    expect(numberToWords(600)).toBe("six hundred");
    expect(numberToWords(700)).toBe("seven hundred");
    expect(numberToWords(800)).toBe("eight hundred");
    expect(numberToWords(900)).toBe("nine hundred");

    expect(numberToWords(101)).toBe("one hundred and one");
    expect(numberToWords(110)).toBe("one hundred and ten");
    expect(numberToWords(119)).toBe("one hundred and nineteen");
    expect(numberToWords(123)).toBe("one hundred and twenty three");
    expect(numberToWords(250)).toBe("two hundred and fifty");
    expect(numberToWords(999)).toBe("nine hundred and ninety nine");

    expect(numberToWords(1000)).toBe("one thousand");
    expect(numberToWords(1001)).toBe("one thousand and one");
    expect(numberToWords(1010)).toBe("one thousand and ten");
    expect(numberToWords(1100)).toBe("one thousand one hundred");
    expect(numberToWords(1110)).toBe("one thousand one hundred and ten");
    expect(numberToWords(1011)).toBe("one thousand and eleven");
    expect(numberToWords(1111)).toBe("one thousand one hundred and eleven");
    expect(numberToWords(5600)).toBe("five thousand six hundred");
    expect(numberToWords(9099)).toBe("nine thousand and ninety nine");
    expect(numberToWords(9999)).toBe(
      "nine thousand nine hundred and ninety nine"
    );

    expect(numberToWords(10000)).toBe("ten thousand");
    expect(numberToWords(20000)).toBe("twenty thousand");
    expect(numberToWords(23000)).toBe("twenty three thousand");
    expect(numberToWords(99999)).toBe(
      "ninety nine thousand nine hundred and ninety nine"
    );

    expect(numberToWords(999999)).toBe(
      "nine hundred and ninety nine thousand nine hundred and ninety nine"
    );

    expect(numberToWords(1000000)).toBe("one million");
    expect(numberToWords(10000000)).toBe("ten million");
    expect(numberToWords(100000000)).toBe("one hundred million");
    expect(numberToWords(888888888)).toBe(
      "eight hundred and eighty eight million eight hundred and eighty eight thousand eight hundred and eighty eight"
    );
  });
});

Tech mentioned