Card 06/ 08

GotchaDifficulty: Intermediate2 min

Sorting Numbers in TypeScript Without a Comparator

[10, 9, 1, 100].sort() returns 1, 10, 100, 9, because the default sort compares numbers as text.

Calling sort() on an array of numbers in TypeScript or JavaScript with no comparator sorts them as text, not by value. This card shows the symptom, why the default does it, and the one-argument fix.

The symptom is a list that looks almost sorted. Prices, ages or scores come out with 100 before 9 and 1000 before 2, and on small test data where every number has one digit nothing looks wrong at all.

default_sort.tsts
const values = [10, 9, 1, 100];
console.log(`default: ${[...values].sort().join(" ")}`);
console.log(`comparator: ${[...values].sort((a, b) => a - b).join(" ")}`);
OutputRan with node:24.22.0-trixie-slim (Node 24.22.0), TypeScript 5.9.3
default: 1 10 100 9
comparator: 1 9 10 100

The trigger is calling sort with no argument on anything but strings. The language specification defines the default comparison for every array, whatever it holds: convert both values to strings and compare the strings code unit by code unit. As strings, "10" and "100" both begin with "1", which comes before "9", so both land ahead of 9, and "10" is a prefix of "100", so it comes first. The result is 1 10 100 9. TypeScript's type checker does not catch it, because sort() with no argument is valid for an array of any type.

The cost is a wrong order that passes casual testing: a leaderboard that ranks 9 points above 100, or a median taken from the wrong item. Python and Java do not share the trap. Python's sorted compares numbers with < and Java's Arrays.sort(int[]) compares numerically, and a run of each on 10 9 1 100 printed 1 9 10 100.

The remedy is to pass a comparator, (a, b) => a - b for ascending order or (a, b) => b - a for descending, which returns a negative number when a belongs first. The second line of the output above shows it giving 1 9 10 100. Pass one every time an array holds numbers, including with toSorted, which has the same default.