jsguides

Set.prototype.union()

set.union(other)

Set.prototype.union() returns a new Set containing every element present in the receiver, the argument, or both. It is the non-mutating equivalent of “give me the set of values across these two collections,” and it ships with the ES2025 batch of new Set methods (intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom).

Syntax

set.union(other)

Parameters

ParameterTypeDescription
otherSet or set-like objectA Set, or any object with a keys() method that yields values. The method reads from other.keys() internally.

Return Value

A new Set containing every value that appears in this, in other, or in both. Duplicates collapse to a single entry, since Set membership already enforces uniqueness. The original Sets are not modified.

Description

In math notation, the result is A ∪ B = { x | x ∈ A or x ∈ B }. The JavaScript implementation matches that definition, with one useful wrinkle: the receiver must be a real Set instance (the spec reads its internal slot directly), while other only needs a keys() method. That asymmetry lets you pass custom set-like objects on the right-hand side, including thin wrappers that yield values from a generator or a Map.

Element equality follows the standard Set rule: SameValueZero. Primitives compare by value, objects compare by reference, and NaN is treated as equal to NaN (unlike ===). Insertion order is preserved, and the order is receiver first, then argument. Values that appear in both stay at their position from the receiver; only values that are missing from the receiver get appended, in the order they were yielded by other.keys().

union() is a thin wrapper around Set semantics. It does not deep-clone elements, so object references are shared. Mutating an object inside the result is visible through the original Sets.

Examples

Combining two Sets

const evens = new Set([2, 4, 6, 8]);
const squares = new Set([1, 4, 9]);

console.log(evens.union(squares));
// Set(6) { 2, 4, 6, 8, 1, 9 }

Notice the order: 2, 4, 6, 8 come from the receiver, then 1, 9 get appended because they were not in evens. The value 4 appears once, anchored at its position in evens. This is the canonical use case, and it reads more clearly than the manual version:

const manual = new Set([...evens, ...squares]);
// Set(6) { 2, 4, 6, 8, 1, 9 }

The manual form produces the same set, but union() expresses intent directly. The native method also runs in C++ inside the engine, which matters when either input is large.

Union with a set-like object

The argument does not have to be a real Set. Anything that exposes a keys() method returning an iterator is accepted:

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

const setLike = {
  keys() {
    let i = 0;
    const vals = [3, 4, 5];
    return {
      next() {
        return i < vals.length
          ? { value: vals[i++], done: false }
          : { value: undefined, done: true };
      }
    };
  }
};

console.log(a.union(setLike));
// Set(5) { 1, 2, 3, 4, 5 }

This is handy when the right-hand side is computed lazily, lives behind an interface you do not own, or is generated from a Map.keys()-shaped object. The receiver still has to be a real Set, though, so you cannot borrow union() from a class that merely imitates one.

Chaining with other Set methods

union() returns a Set, so it composes with the other ES2025 methods:

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

console.log(a.union(b).union(c));
// Set(7) { 1, 2, 3, 4, 5, 6, 7 }

Chained calls merge in the same “receiver first” order: a anchors the front, then b appends, then c appends. For three or more sets this is a clean way to express the combined membership without writing nested spreads.

Common Patterns

Merging tag or permission lists

union() is a natural fit whenever you need the combined set of values from two or more sources:

const userTags = new Set(['javascript', 'typescript']);
const orgTags = new Set(['javascript', 'react', 'node']);

const effectiveTags = userTags.union(orgTags);
console.log(effectiveTags);
// Set(4) { 'javascript', 'typescript', 'react', 'node' }

The same shape works for permission sets, environment variables, allowed origins, and any other “merge the membership of two collections” use case. If you need the overlap or the difference, reach for intersection() and difference() instead.

Polyfill for older runtimes

union() is ES2025, so on engines that predate the Set methods proposal (Chrome < 122, Firefox < 127, Safari < 17, Node < 22) calling it throws TypeError: set.union is not a function. If you need to support those runtimes, a small polyfill is enough:

if (!Set.prototype.union) {
  Set.prototype.union = function (other) {
    if (typeof other?.keys !== 'function') {
      throw new TypeError('union() requires a Set or set-like object');
    }
    const out = new Set(this);
    for (const v of other.keys()) out.add(v);
    return out;
  };
}

This is the same logic the native method runs, expressed in plain JavaScript. For a more complete solution that also covers intersection, difference, and friends, use core-js (core-js/features/set/union) or the es-shims/set.prototype.union package.

See Also