To get distinct values from an array in TypeScript, pass the array to a Set and spread it back: [...new Set(arr)]. A Set keeps each value once, in first-seen order, and TypeScript keeps the element type, so a string[] stays a string[]:
const distinct = [...new Set(states)]; // ["TX", "CA", "NY", "FL"]
I compiled these snippets with TypeScript 7.0.2 running on Node.js v22.22.2. One of them, the Set-methods example, also needs the ES2025 library turned on.
You’ll see the Set one-liner and the filter alternative, distinct values ignoring case, distinct values of one property with counts, comparing two lists, and how the approaches scale.
Get distinct values with new Set()
Set uses SameValueZero equality, so strings and numbers work as you’d expect. The filter plus indexOf version gives the same result for normal values but not for NaN:
const states: string[] = ["TX", "CA", "TX", "NY", "CA", "FL", "TX"];
const distinct: string[] = [...new Set(states)];
console.log(distinct);
console.log(Array.from(new Set(states)).length, "distinct of", states.length);
const viaFilter = states.filter((s, i, arr) => arr.indexOf(s) === i);
console.log(viaFilter);
const readings = [1, NaN, 2, NaN, 2];
console.log("Set: ", [...new Set(readings)]);
console.log("filter:", readings.filter((v, i, arr) => arr.indexOf(v) === i));
Output:
[ 'TX', 'CA', 'NY', 'FL' ]
4 distinct of 7
[ 'TX', 'CA', 'NY', 'FL' ]
Set: [ 1, NaN, 2 ]
filter: [ 1, 2 ]

indexOf(NaN) is always -1, so every NaN fails the test. Array.from(set) and the spread give the same array; the trade-offs are in converting a Set to an array in TypeScript.
How do I get distinct values ignoring case and spaces?
A Set compares values exactly, so "Austin" and "austin " both survive. Use a normalized key in a Map and keep the first spelling you saw:
const cities = ["Austin", "austin ", "AUSTIN", "Denver", "denver", "Boise"];
const seen = new Map<string, string>();
for (const city of cities) {
const key = city.trim().toLowerCase();
if (!seen.has(key)) seen.set(key, city.trim()); // keep the first spelling
}
console.log([...seen.values()]);
Output:
[ 'Austin', 'Denver', 'Boise' ]
trim() and toLowerCase() build the key; the Map value keeps the original text for display. For the case conversion itself, see converting a string to lowercase in TypeScript.
Get distinct values of a property, with counts
For an array of objects, map to the property first, then use a Set. When you also need how often each value appears, count into a Map:
interface Order { id: number; state: string; total: number }
const orders: Order[] = [
{ id: 1, state: "TX", total: 120.0 },
{ id: 2, state: "CA", total: 89.99 },
{ id: 3, state: "TX", total: 45.5 },
{ id: 4, state: "NY", total: 230.25 },
{ id: 5, state: "CA", total: 15.0 },
];
const states = [...new Set(orders.map((o) => o.state))].sort();
console.log("distinct states:", states);
const counts = new Map<string, number>();
for (const o of orders) counts.set(o.state, (counts.get(o.state) ?? 0) + 1);
console.log(counts);
console.log("states with repeat orders:", [...counts].filter(([, n]) => n > 1).map(([s]) => s));
Output:
distinct states: [ 'CA', 'NY', 'TX' ]
Map(3) { 'TX' => 2, 'CA' => 2, 'NY' => 1 }
states with repeat orders: [ 'TX', 'CA' ]

To keep whole objects rather than one field, use the key-based approach in getting unique values from an array of objects.
Compare two arrays with Set union, intersection and difference
ES2025 added set operations to Set. They need --lib es2025 in TypeScript and Node.js 22 or a current browser:
const mondayVisitors = new Set(["emily", "james", "olivia", "liam"]);
const tuesdayVisitors = new Set(["james", "ava", "liam", "noah"]);
console.log("either day: ", [...mondayVisitors.union(tuesdayVisitors)]);
console.log("both days: ", [...mondayVisitors.intersection(tuesdayVisitors)]);
console.log("only Monday: ", [...mondayVisitors.difference(tuesdayVisitors)]);
console.log("only one day:", [...mondayVisitors.symmetricDifference(tuesdayVisitors)]);
Output:
either day: [ 'emily', 'james', 'olivia', 'liam', 'ava', 'noah' ]
both days: [ 'james', 'liam' ]
only Monday: [ 'emily', 'olivia' ]
only one day: [ 'emily', 'olivia', 'ava', 'noah' ]

Before ES2025 you’d write a.filter(x => b.has(x)). To test whether two lists hold exactly the same values, see checking array equality in TypeScript.
Remove duplicates from an array in place
Sometimes other code holds a reference to the same array, so you can’t hand back a new one. Replace the contents in place with splice(), or overwrite and trim with length:
const skus: string[] = ["A-100", "B-220", "A-100", "C-310", "B-220", "D-415"];
const shared = skus; // another part of the app holds this reference
skus.splice(0, skus.length, ...new Set(skus));
console.log(skus, "same array:", shared === skus);
const codes = ["TX", "CA", "TX", "NY", "CA", "FL"];
const before = codes;
const seen = new Set<string>();
let write = 0;
for (const c of codes) {
if (!seen.has(c)) { seen.add(c); codes[write++] = c; }
}
codes.length = write; // trim the leftovers
console.log(codes, "same array:", before === codes);
Output:
[ 'A-100', 'B-220', 'C-310', 'D-415' ] same array: true
[ 'TX', 'CA', 'NY', 'FL' ] same array: true

Both keep the first occurrence of each value. The splice arguments are explained in the TypeScript array splice method.
Which distinct method is fastest?
Here’s 50,000 ZIP codes with 5,000 distinct values. filter plus indexOf rescans the array for every element:
const zips: string[] = Array.from({ length: 50_000 }, (_, i) => String(10000 + ((i * 7919) % 5000)));
function time(label: string, fn: () => string[]): void {
const start = performance.now();
const out = fn();
console.log(`${label.padEnd(16)} ${out.length} distinct ${(performance.now() - start).toFixed(1)} ms`);
}
time("new Set", () => [...new Set(zips)]);
time("filter+indexOf", () => zips.filter((z, i, a) => a.indexOf(z) === i));
Output:
new Set 5000 distinct 1.6 ms
filter+indexOf 5000 distinct 270.5 ms

Your timings will differ, but the Set stays roughly linear while indexOf grows with the square of the size. Checking membership the same way is covered in checking if a value exists in an array.
Distinct value methods compared
| Goal | Code |
|---|---|
| Distinct primitives | [...new Set(arr)] |
| Old-style, no Set | arr.filter((v, i, a) => a.indexOf(v) === i) |
| Ignore case | Map keyed by v.trim().toLowerCase() |
| One property | [...new Set(arr.map(o => o.state))] |
| Values with counts | Map<value, number> |
| Dedupe the same array | arr.splice(0, arr.length, ...new Set(arr)) |
| In both / either array | a.intersection(b), a.union(b) |
Next, you could read about these array topics:
- Remove duplicates from an array of objects
- Remove undefined values from an array
- Find the length of an array in TypeScript
- Merge arrays of objects in TypeScript
Frequently asked questions
How do I get distinct values from an array in TypeScript?
Use [...new Set(arr)] or Array.from(new Set(arr)).
Why does filter with indexOf drop NaN?
indexOf(NaN) returns -1 because NaN never equals itself. A Set treats NaN as one value.
How do I get distinct values case-insensitively?
Key a Map by the lowercased, trimmed value and keep the first original spelling.
How do I remove duplicates from an array without creating a new one?
Use arr.splice(0, arr.length, ...new Set(arr)), which keeps the same array reference.
How do I get distinct values from an array of objects?
Map to the property, then use a Set: [...new Set(orders.map(o => o.state))].
Where is Set documented?
See Set 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