TypeScript Set: HashSet Equivalent, Methods and Examples

A TypeScript Set stores unique values in insertion order. Create one, add and check values, remove duplicates from arrays, and compare sets with the ES2025 methods.

angular-typescript

A TypeScript Set is a collection of unique values, so adding a value that is already present does nothing. It is the standard JavaScript Set class with a type parameter, so Set<string> accepts only strings and Set<number> accepts only numbers.

We use a Set to remove duplicates from a list and to check whether a value has been seen before, such as the email addresses that already got a newsletter. Since ES2025, a Set also compares two groups of values with union, intersection and difference.

Java developers often search for a TypeScript HashSet, but there is no separate class. The built-in Set is a hash-based set that also keeps the values in insertion order, like Java’s LinkedHashSet. Unlike Java, it has no equals() or hashCode() to override, which matters when we put objects in it.

The following example shows the common Set tasks in one snippet, tested with TypeScript 7.0.2 on Node.js 22. The set operations in step 6 need Node.js 22 or a current browser, and the lib setting from section 5.1.

const fruits = new Set<string>();

// 1. Add values; a duplicate is ignored
fruits.add("apple").add("banana").add("apple");         // fruits = {"apple", "banana"}

// 2. Check a value
const hasApple = fruits.has("apple");                   // hasApple = true

// 3. Size of the Set
const count = fruits.size;                              // count = 2

// 4. Iterate in insertion order
for (const fruit of fruits) {
  console.log(fruit);                                   // apple, banana
}

// 5. Remove duplicates from an array
const unique = [...new Set([1, 2, 2, 3, 3])];           // unique = [1, 2, 3]

// 6. Set operations (ES2025)
const a = new Set([1, 2, 3]);
const b = new Set([3, 4]);
const all = a.union(b);                                 // all = {1, 2, 3, 4}
const common = a.intersection(b);                       // common = {3}
const onlyA = a.difference(b);                          // onlyA = {1, 2}

// 7. Delete a value and clear the Set
const isDeleted = fruits.delete("banana");              // isDeleted = true
fruits.clear();                                         // size = 0

Notice that the second “apple” in step 1 is ignored, so the size is 2. In the comments, {“apple”, “banana”} stands for a Set holding those values, and Node.js prints it as Set(2) { ‘apple’, ‘banana’ }.

The sections that follow cover creating and typing a Set, iteration, conversion to arrays and JSON, and the ES2025 set methods. After that, we see how a Set decides that two values are duplicates and when the ReadonlySet and WeakSet types help.

1. Creating a Set and Typing Its Values

We create a Set with new Set(), whose constructor accepts any iterable, such as an array, a string, another Set or the keys of a Map. The constructor drops duplicates in the input. When the input has values, TypeScript infers the element type, whereas an empty Set needs the type in angle brackets.

// 1. Empty Set with an explicit type
const names = new Set<string>();

// 2. From an array; the type is inferred as Set<number>
const nums = new Set([1, 2, 2, 3]);                     // nums = {1, 2, 3}

// 3. From a string: one entry per character
const letters = new Set("hello");                       // letters = {"h", "e", "l", "o"}

// 4. From a union type
type Color = "red" | "green" | "blue";
const colors = new Set<Color>(["red", "blue"]);

Without a type parameter and without initial values, new Set() has the type Set<unknown>. TypeScript accepts any value in add(), but every value read from the Set is unknown and must be checked before use. Mixed initial values give a union type, so new Set([1, “a”]) is a Set<string | number>.

Option 4 restricts the Set to the three color names. Calling colors.add(“pink”) is a compile error, which catches typos in values that come from a fixed list. For example, a product filter that stores the selected colors in a Set<Color> can never hold a color the shop does not sell.

2. add(), has(), delete() and size

A Set has four methods for single values and one property. The lookup with has() is fast even for large sets, because the ECMAScript specification requires access times that are sublinear on average and engines use hash tables. An array’s includes(), in contrast, checks the elements one by one.

MemberWhat it doesReturns
add(value)Adds the value if it is not already presentThe Set itself, so calls can be chained
has(value)Checks whether the value is presenttrue or false
delete(value)Removes the valuetrue if it was present, otherwise false
clear()Removes all valuesundefined
sizeNumber of values (a property, not a method)A number
const fruits = new Set<string>();

// 1. Add; add() returns the Set, so calls can be chained
fruits.add("apple");
fruits.add("banana").add("mango");
fruits.add("apple");                                    // ignored, already present

// 2. Read
const count = fruits.size;                              // count = 3
const hasMango = fruits.has("mango");                   // hasMango = true
const hasKiwi = fruits.has("kiwi");                     // hasKiwi = false

// 3. Delete
const deleted = fruits.delete("mango");                 // deleted = true
const again = fruits.delete("mango");                   // again = false

// 4. Clear
fruits.clear();                                         // size = 0

A Set has no get() method and no index. A value is either in the Set or not, so has() is the only lookup. When we need to read data stored for a key, a Map is the right collection. The size property is the counterpart of an array’s length; fruits.length is a compile error.

3. Looping Through a Set

A Set returns its values in the sequence they were first added (insertion order), and the most common loop is for…of. The methods forEach(), values(), keys() and entries() also work, and the last three exist so that a Set has the same iteration methods as a Map.

const fruits = new Set(["apple", "banana", "mango"]);

// 1. for...of
for (const fruit of fruits) {
  console.log(fruit);                                   // apple, banana, mango
}

// 2. forEach: value and "key" are the same value
fruits.forEach((value) => console.log(value));          // apple, banana, mango

// 3. values(), keys() and entries()
const values = [...fruits.values()];                    // values = ["apple", "banana", "mango"]
const entries = [...fruits.entries()];                  // entries[0] = ["apple", "apple"]

// 4. Re-adding keeps the position; delete and add moves it to the end
fruits.add("apple");                                    // order unchanged
fruits.delete("apple");
fruits.add("apple");
const order = [...fruits];                              // order = ["banana", "mango", "apple"]

The forEach() callback receives the value twice, as the first and second arguments, because a Set has no separate keys. In the same way, keys() returns the values, and each pair from entries() holds the value twice. Some tutorials state that a Set has no order, but in JavaScript the specification defines the order.

Insertion order is useful in a “recently viewed products” list. When we delete a product ID and add it again on every view, the Set keeps each product once and puts the latest one at the end.

4. Set to Array, Array to Set and JSON

A Set has no array methods such as map() and sort(). To use them, we convert the Set with the spread syntax or Array.from(), and build a new Set from the result when we need one. Converting an array to a Set and back is the shortest way to remove duplicates.

const fruits = new Set(["apple", "banana"]);

// 1. Set to array
const arr1 = [...fruits];                               // arr1 = ["apple", "banana"]
const arr2 = Array.from(fruits);                        // arr2 = ["apple", "banana"]

// 2. Remove duplicates from an array
const unique = [...new Set(["apple", "kiwi", "apple"])];   // unique = ["apple", "kiwi"]

// 3. Use a Set for fast lookups while filtering
const sold = new Set(["apple"]);
const unsold = ["apple", "banana", "kiwi"].filter((f) => !sold.has(f));   // unsold = ["banana", "kiwi"]

// 4. JSON: convert to an array first
const wrong = JSON.stringify(fruits);                   // wrong = {}
const json = JSON.stringify([...fruits]);               // json = ["apple","banana"]
const restored = new Set<string>(JSON.parse(json));     // restored = {"apple", "banana"}

The deduplicated array keeps the first position of each value, so unique lists “apple” before “kiwi”. Option 3 is a common performance fix. When a filter() callback checks membership in a long list, building a Set once turns each check from a scan into a single lookup. For example, a shop page that hides the products already in the user’s order history builds one Set of those IDs instead of scanning the history for every product.

The wrong line fails without any error message. The method JSON.stringify() serializes the enumerable properties that belong to the object itself, and a Set stores its values elsewhere, so the result is {}. We always convert the Set to an array before sending or storing it.

5. Union, Intersection and Difference With the ES2025 Set Methods

ECMAScript 2025 added seven methods for comparing sets. Four of them return a new Set and the other three return a boolean. None of the seven methods changes the Set it is called on. Before ES2025, projects wrote their own helpers for the same operations.

A recipe app is a typical case. The intersection of the recipe’s ingredients and the user’s pantry shows what the user already has, and the difference shows the shopping list.

const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5]);

// 1. Methods that return a new Set
const all = a.union(b);                                 // all = {1, 2, 3, 4, 5}
const common = a.intersection(b);                       // common = {3, 4}
const onlyA = a.difference(b);                          // onlyA = {1, 2}
const notBoth = a.symmetricDifference(b);               // notBoth = {1, 2, 5}

// 2. Methods that return a boolean
const sub = new Set([1, 2]).isSubsetOf(a);              // sub = true
const sup = a.isSupersetOf(new Set([1, 2]));            // sup = true
const disjoint = a.isDisjointFrom(new Set([7, 8]));     // disjoint = true

// 3. The argument can be a Map; its keys are used
const withMap = a.intersection(new Map([[4, "four"]])); // withMap = {4}
MethodResultValues in the result
union(b)New SetIn a or in b
intersection(b)New SetIn both a and b
difference(b)New SetIn a but not in b
symmetricDifference(b)New SetIn only one of the two
isSubsetOf(b)Booleantrue if every value of a is in b
isSupersetOf(b)Booleantrue if every value of b is in a
isDisjointFrom(b)Booleantrue if a and b share no value

The argument does not have to be a Set. It must be “set-like”, which means an object with a size property plus the methods has() and keys(), so a Map works in option 3. An array is not set-like. Passing [3, 4] is a compile error (TS2739, because an array has no has() method or size property) and a TypeError at runtime, so we wrap an array in new Set() first.

5.1. Runtime and Compiler Requirements

The methods run on Node.js 22.0.0 and later, Chrome 122, Firefox 127 and Safari 17, according to the MDN compatibility data. On Node.js 20 or older browsers, a.union is undefined, and calling it throws a TypeError.

The compiler side needs one setting. TypeScript first added the types in TypeScript 5.5, and TypeScript 7.0.2 declares them in the es2025.collection library. A project whose lib setting stops at ES2024 does not see the set methods and fails with error TS2550.

src/err.ts(3,15): error TS2550: Property 'union' does not exist on type 'Set<number>'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2025' or later.

The error suggests “lib”: [“es2025”]. That works for compiling, but the full ES2025 library also declares features Node.js 22 does not have, such as Float16Array, so the compiler would accept code that fails at runtime. For a Node.js 22 project, we add only the collection part.

{
  "compilerOptions": {
    "target": "es2024",
    "lib": ["es2024", "es2025.collection"]
  }
}

5.2. Set Operations on Older Runtimes

Code that must also run on Node.js 20 or older browsers can build the same results from the spread syntax, filter() and has(). The results match the ES2025 methods, but each line creates temporary arrays.

const all = new Set([...a, ...b]);                      // all = {1, 2, 3, 4, 5}
const common = new Set([...a].filter((x) => b.has(x))); // common = {3, 4}
const onlyA = new Set([...a].filter((x) => !b.has(x))); // onlyA = {1, 2}

6. Which Values Count as Duplicates

A Set compares values with the SameValueZero rule from the ECMAScript specification. The SameValueZero rule works like ===, with the one difference that NaN equals NaN. Primitive values such as strings and numbers are compared by value. Objects and arrays are compared by reference, so two objects with the same fields are two different values.

interface Person {
  name: string;
  age: number;
}

// 1. NaN equals NaN; 0 and -0 are the same value
const special = new Set([NaN, NaN, 0, -0]);             // special = {NaN, 0}

// 2. 1 and "1" are different values
const mixed = new Set<number | string>([1, "1"]);       // mixed.size = 2

// 3. Objects are compared by reference
const people = new Set<Person>();
people.add({ name: "Lokesh", age: 37 });
people.add({ name: "Lokesh", age: 37 });                // people.size = 2

const raj: Person = { name: "Raj", age: 35 };
const team = new Set<Person>([raj, raj]);               // team.size = 1

Java’s HashSet would call equals() and hashCode() here, but JavaScript has no equality hook for objects. For example, a contact import that reads the same customer twice from a CSV file creates two objects, and a Set keeps both. To keep objects unique by a field, we key them by that field. A Map from the field to the object keeps one object per key, and a Set of the field values tracks which keys we have seen.

const list: Person[] = [
  { name: "Lokesh", age: 37 },
  { name: "Raj", age: 35 },
  { name: "Lokesh", age: 38 },
];

// 1. Unique by name: a Map keyed by the name (last one wins)
const byName = new Map(list.map((p) => [p.name, p]));
const unique = [...byName.values()];                    // unique = [Lokesh 38, Raj 35]

// 2. Only the first person with each name
const seen = new Set<string>();
const firsts = list.filter((p) => {
  if (seen.has(p.name)) return false;
  seen.add(p.name);
  return true;
});                                                     // firsts = [Lokesh 37, Raj 35]

The two options keep different objects for the same name.

  • Option 1 keeps the last object for each name, because a later set() on the same key replaces the value but keeps the key’s position.
  • Option 2 keeps the first one.

For objects without one identifying field, a string key such as p.name + “|” + p.age works the same way. When many places in the code need unique objects, we move these lines into a small generic function that takes the key function as a parameter.

7. ReadonlySet and WeakSet

TypeScript has a ReadonlySet<T> type that contains only the reading members, such as has(), size, iteration and the ES2025 methods. We use it for function parameters and for exported constants that other code must not change, such as the list of supported currencies in a payment module. The ReadonlySet type exists only at compile time, and the object at runtime is still a normal Set.

// 1. ReadonlySet: no add(), delete() or clear() in the type
const colors: ReadonlySet<string> = new Set(["red", "green"]);
const hasRed = colors.has("red");                       // hasRed = true

// 2. WeakSet: objects only, no size, no iteration
const visited = new WeakSet<object>();
const page = { url: "/home" };
visited.add(page);
const seen = visited.has(page);                         // seen = true

Calling colors.add(“blue”) fails with “error TS2339: Property ‘add’ does not exist on type ‘ReadonlySet<string>’.”

A WeakSet holds objects without keeping them alive. When no other code references an object, the garbage collector can remove the object, and the object disappears from the WeakSet. Because the contents can change at any time, a WeakSet has no size and cannot be iterated. We use it to mark objects, for example “already processed”, without causing a memory leak.

8. When to Use a Set Instead of an Array

Both collections hold a list of values, and both keep insertion order. The choice depends on whether duplicates are allowed and on which operation runs most often.

NeedSetArray
DuplicatesNever storedAllowed
Check whether a value existshas(), fast for any sizeincludes(), scans the elements
Access by positionNot possiblearr[i]
Sorting, map(), filter()Convert to an array firstBuilt in
JSONConvert to an array firstWorks as is
Union, intersection, differenceBuilt-in methods (ES2025)Write with filter()

We use a Set when values must be unique or when the code mostly asks “is this value present?”. We use an array when we need access by index, duplicates or array methods. Many programs use both, with an array for the data and a Set built from it for fast lookups.

9. Building and Running the Code

The sets folder on GitHub repeats each snippet from this guide in a separate file. Node.js 22 is the minimum version, because older releases lack the ES2025 set methods, and the compiler is TypeScript 7.0.2. Its tsconfig.json adds the es2025.collection library as described in section 5.1.

{
  "compilerOptions": {
    "target": "es2024",
    "lib": ["es2024", "es2025.collection"],
    "module": "nodenext",
    "strict": true,
    "rootDir": ".",
    "outDir": "dist",
    "types": ["node"],
    "skipLibCheck": true
  },
  "include": ["src"]
}
npm install
npm start

The output prints each Set the way Node.js shows it, for example Set(3) { 1, 2, 5 } for the symmetric difference.

10. Conclusion

A TypeScript Set is a typed collection of unique values that keeps insertion order. We create it with new Set<T>() or from an array, work with add(), has(), delete() and size, and convert it to an array for sorting, mapping, filtering and JSON. On Node.js 22 and current browsers, the ES2025 methods union(), intersection(), difference(), symmetricDifference(), isSubsetOf(), isSupersetOf() and isDisjointFrom() replace hand-written helpers, once the lib setting includes es2025.collection. Objects are unique by reference only, so we key them by a field when value equality is needed.

11. References

MDN documents the runtime behavior and browser support of Set; the release notes of TypeScript 5.5 describe when the compiler added the set method types.

Happy Learning !!

Source Code on Github

Leave a Comment

  1. While I do appreciate the help for function names (nice job!), I think it would nice to have something that illustrates the idea that sets only allow distinct values in the example. Perhaps add another 1 to the set and then call size should suffice.

    Thanks!

    • Hi Mike, Thanks for the feedback. Set allows distinct values.. is written in post introduction. I am assuming that people who reach this article will already know what exactly a set is (theoretically). This post is to help those who are looking for exact function names (as you pointed out) in typescript.

  2. Hello, for the ‘Iterate over Set’ exemple, how can i set the define the type of currentNumber ?

    Thanks !

  3. There is an error in your documentation. List item 1 should read set.add() – method to add values in the Set

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.