To sort an array alphabetically in TypeScript, call sort() with localeCompare(): names.sort((a, b) => a.localeCompare(b)). Without it, sort() compares character codes, so capitals come before lowercase letters and accented names go last:
const sorted = cities.toSorted((a, b) => a.localeCompare(b));
I wanted the sorted lists here to be exactly what you’ll get, so each one was printed by TypeScript 7.0.2 under Node.js v22.22.2 in strict mode.
You’ll see why the default sort is wrong for text, localeCompare options for case, accents and numbers, Z-A order, arrays of objects by name, the readonly error and a faster collator.
Why does sort() put uppercase strings first?
Without a compare function, sort() orders strings by UTF-16 code units. Every capital letter comes before every lowercase one, accented letters go last, and digits compare one character at a time:
const cities: string[] = ["denver", "Austin", "Érie", "boston", "Chicago", "albany"];
console.log([...cities].sort());
const files: string[] = ["report10.csv", "report9.csv", "report1.csv", "report2.csv"];
console.log([...files].sort());
Output:
[ 'Austin', 'Chicago', 'albany', 'boston', 'denver', 'Érie' ]
[ 'report1.csv', 'report10.csv', 'report2.csv', 'report9.csv' ]

That’s fine for IDs and codes, but not for anything a person reads. For sorting in general, including numbers, see sorting arrays in TypeScript.
Sort an array alphabetically with localeCompare() options
localeCompare() uses real language rules. Its options handle the cases that matter for strings: sensitivity for case and accents, and numeric for numbers inside text:
const cities: string[] = ["denver", "Austin", "Érie", "boston", "Chicago", "albany"];
console.log(cities.toSorted((a, b) => a.localeCompare(b)));
const names = ["emily", "Emily", "EMILY", "émily"];
console.log(names.toSorted((a, b) => a.localeCompare(b, "en", { sensitivity: "base" })));
console.log("base equal?", "emily".localeCompare("Émily", "en", { sensitivity: "base" }) === 0);
const files: string[] = ["report10.csv", "report9.csv", "report1.csv", "report2.csv"];
console.log(files.toSorted((a, b) => a.localeCompare(b, undefined, { numeric: true })));
console.log("descending:", cities.toSorted((a, b) => b.localeCompare(a)));
console.log("original untouched:", cities);
Output:
[ 'albany', 'Austin', 'boston', 'Chicago', 'denver', 'Érie' ]
[ 'emily', 'Emily', 'EMILY', 'émily' ]
base equal? true
[ 'report1.csv', 'report2.csv', 'report9.csv', 'report10.csv' ]
descending: [ 'Érie', 'denver', 'Chicago', 'boston', 'Austin', 'albany' ]
original untouched: [ 'denver', 'Austin', 'Érie', 'boston', 'Chicago', 'albany' ]

toSorted() (ES2023) returns a new array, so cities keeps its order; sort() changes the array in place. Flip a and b for descending. For numbers stored as numbers, see sorting a number array in descending order.
Sort an array of objects alphabetically by name
For objects, compare the property you want. Chain a second localeCompare with || to break ties, such as first name after last name:
interface Customer { first: string; last: string; city: string }
const customers: Customer[] = [
{ first: "Olivia", last: "Brown", city: "Tampa" },
{ first: "James", last: "wilson", city: "Denver" },
{ first: "Ava", last: "Brown", city: "Austin" },
{ first: "Liam", last: "Álvarez", city: "Reno" },
];
const byName = customers.toSorted(
(a, b) => a.last.localeCompare(b.last, "en", { sensitivity: "base" }) || a.first.localeCompare(b.first),
);
byName.forEach((c) => console.log(`${c.last}, ${c.first} (${c.city})`));
const byCityDesc = customers.toSorted((a, b) => b.city.localeCompare(a.city));
console.log("cities Z-A:", byCityDesc.map((c) => c.city));
Output:
Álvarez, Liam (Reno)
Brown, Ava (Austin)
Brown, Olivia (Tampa)
wilson, James (Denver)
cities Z-A: [ 'Tampa', 'Reno', 'Denver', 'Austin' ]

|| only uses the second rule when the last names are equal. More property sorts, including numbers and dates, are in sorting an array of objects by property value.
Fix the sort error on a readonly string array
Constants are often typed readonly string[]. Calling sort() on one fails, because it would mutate the array:
const STATES: readonly string[] = ["Texas", "Ohio", "Iowa", "Utah"];
STATES.sort();
Output:
sort_readonly.ts(3,8): error TS2339: Property 'sort' does not exist on type 'readonly string[]'.

Use STATES.toSorted() or [...STATES].sort() instead; both leave the original alone. There’s more on this type in readonly arrays in TypeScript.
Sort large string arrays faster with Intl.Collator
localeCompare() with options sets up the comparison rules on every call. An Intl.Collator builds them once, which adds up on big arrays:
const firstNames = ["James", "Olivia", "Liam", "Emma", "Noah", "Ava", "Mason", "Sophia"];
const big: string[] = Array.from({ length: 50_000 }, (_, i) => firstNames[i % 8] + (i * 7919) % 100_000);
const collator = new Intl.Collator("en", { numeric: true, sensitivity: "base" });
function time(label: string, fn: () => string[]): void {
const start = performance.now();
const out = fn();
console.log(`${label.padEnd(22)} first: ${out[0].padEnd(8)} ${(performance.now() - start).toFixed(0)} ms`);
}
time("localeCompare(...opts)", () => big.toSorted((a, b) => a.localeCompare(b, "en", { numeric: true, sensitivity: "base" })));
time("Intl.Collator compare", () => big.toSorted(collator.compare));
time("default sort()", () => big.toSorted());
Output:
localeCompare(...opts) first: Ava19 3792 ms
Intl.Collator compare first: Ava19 113 ms
default sort() first: Ava10003 12 ms

Your timings will vary, but I create one collator per sort rule and reuse it. When the strings live inside objects, pass collator.compare(a.last, b.last) to the same compare function shown above.
For lists too long to show at once, sort first and then page through them with splitting an array in TypeScript.
Sort a string array by length or with missing values
A compare function can rank by anything. Here strings are sorted by length with alphabetical tie-breaks, then an array with blanks and undefined is sorted two ways:
const products = ["Mug", "Coffee beans", "Tea", "Filters", "French press"];
const byLength = products.toSorted((a, b) => a.length - b.length || a.localeCompare(b));
console.log(byLength);
const withBlanks: (string | undefined)[] = ["Reno", undefined, "Akron", "", "Mesa"];
console.log(withBlanks.toSorted((a, b) => (a ?? "").localeCompare(b ?? "")));
console.log([...withBlanks].sort()); // default sort moves undefined to the end
Output:
[ 'Mug', 'Tea', 'Filters', 'Coffee beans', 'French press' ]
[ '', 'Akron', 'Mesa', 'Reno', undefined ]
[ '', 'Akron', 'Mesa', 'Reno', undefined ]
|| falls through to the second rule only when the lengths are equal. Both blank-value sorts match: sort() moves undefined to the end without calling your function, but strict TypeScript still wants the ?? "" guard.
Empty strings, on the other hand, sort first. To strip empty strings before sorting, use removing empty strings from an array.
Alphabetical sorting options compared
| Goal | Compare function or method |
|---|---|
| Human A–Z order | (a, b) => a.localeCompare(b) |
| Ignore case and accents | localeCompare(b, "en", { sensitivity: "base" }) |
| file2 before file10 | localeCompare(b, undefined, { numeric: true }) |
| Z–A | (a, b) => b.localeCompare(a) |
| Objects by name | (a, b) => a.last.localeCompare(b.last) |
| Keep the original | toSorted() or [...arr].sort() |
| Big arrays | arr.sort(new Intl.Collator("en").compare) |
Sorting is rarely the last step, so here’s where to go next:
- Type an array of objects with an interface
- Sort an array by date in TypeScript
- Get distinct values from an array in TypeScript
- Reverse an array in TypeScript
Frequently asked questions
How do I sort an array alphabetically in TypeScript?
Use arr.sort((a, b) => a.localeCompare(b)), or toSorted() to keep the original array.
Why does sort() put “Zoe” before “adam”?
The default sort compares UTF-16 code units, and all uppercase letters come before lowercase ones.
How do I sort strings case-insensitively?
Pass { sensitivity: "base" } to localeCompare(), which treats a, A and á as equal.
How do I sort strings with numbers in natural order?
Use localeCompare(b, undefined, { numeric: true }) so “file2” sorts before “file10”.
How do I sort an array of objects alphabetically by name?
Compare the property: people.sort((a, b) => a.last.localeCompare(b.last)), adding a second localeCompare for ties.
Where is String.prototype.localeCompare documented?
See String.prototype.localeCompare() on MDN.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn