Внезапный программистский контент:
TLDR: Если в JS/TS нужно отсортировать массив чисел, которые представлены строками, то можно использовать localeCompare с параметром numeric: true.
Возможно вы знаете что JS:
Это сделано чтобы можно было сортировать списки имен/названий:
['John', 'Ben', 'Alice'].sort() // ['Alice', 'Ben', 'John']
У этого есть отдельные проблемы, например буква Ё не находится между буквами Е и Ж:
['Лиса', 'Ёж', 'Волк', 'Аист'].sort() // ['Ёж', 'Аист', 'Волк', 'Лиса']
Но так же это не работает нормально, если у вас числа представлены строками:
['10','2','1'].sort() // ['1', '10', '2']
И в целом это даже логично, так как строго следует простому алгоритму. Но неудобно в реальности.
Можно конечно явно описать функцию сравнения, и там преобразовывать строки в числа:
['10','2','1'].sort((a, b) => a - b) // ['1', '2', '10']
Но у такого подхода есть огромная проблема: числа могут выйти за пределы безопасных значений и все сломается:
['9007199254740993','9007199254740991','9007199254740992'].sort((a, b) => a - b) // ['9007199254740991', '9007199254740993', '9007199254740992']
А такое может случиться, если на бэкенде используются int64 для id-шников сущностей. В целом иногда сортировать большие числа все-таки надо, поэтому в актуальном JS-е есть метод localCompare, который умеет это делать!
116457 < 3085 // false
"116457" < "3085" // true
"116457".localeCompare("3085") // -1
"116457".localeCompare("3085", undefined, { numeric: true }) // 1
['10','2','1'].sort((a, b) => a.localeCompare(b, undefined, { numeric: true })) // ['1', '2', '10']
['9007199254740993','9007199254740991','9007199254740992'].sort((a, b) => a.localeCompare(b, undefined, { numeric: true })) // ['9007199254740991', '9007199254740992', '9007199254740993']
Описание на MDN