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:

• Let the recursion do the work.
• Handle “and” carefully.
• Don’t move up in factors of ten; move up in word groups.
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"
);
});
});