JavaScript Set Methods Accept Set-Like Objects, Not Arbitrary Iterables
Use union, intersection, difference, and Set predicates with the size, has, and keys set-like protocol.
JavaScript’s set-composition methods require a real Set as the receiver, but their argument may be any set-like object. Set-like does not mean iterable. The object must expose a non-negative numeric size, a callable has(value), and a callable keys() that returns an iterator.
That contract lets a Set interoperate directly with a Map, whose keys already form a set-like view. It also lets a library adapt an indexed collection without copying it first.
The seven methods landed in ECMAScript 2025: union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, and isDisjointFrom. Older runtimes need a feature check or a spec-compatible polyfill.
Four methods return new sets
The ECMAScript composition methods do not mutate the receiver or argument.
const left = new Set(['a', 'b', 'c']);
const right = new Set(['b', 'c', 'd']);
console.log([...left.union(right)]); // ['a', 'b', 'c', 'd']
console.log([...left.intersection(right)]); // ['b', 'c']
console.log([...left.difference(right)]); // ['a']
console.log([...left.symmetricDifference(right)]); // ['a', 'd']
console.log([...left]); // ['a', 'b', 'c']
console.log([...right]); // ['b', 'c', 'd']
difference is directional. left.difference(right) keeps values found only in left; reversing the call keeps values found only in right. symmetricDifference contains values found in exactly one side.
The returned value is an ordinary Set built by the intrinsic constructor semantics. These methods do not consult Symbol.species, so calling one on a Set subclass does not promise an instance of that subclass.
Three methods return booleans
The Set predicates answer relationship questions without creating a result collection.
const allowed = new Set(['read', 'write', 'delete']);
const requested = new Set(['read', 'write']);
const guest = new Set(['preview']);
console.log(requested.isSubsetOf(allowed)); // true
console.log(allowed.isSupersetOf(requested)); // true
console.log(allowed.isDisjointFrom(guest)); // true
These names describe the receiver relative to the argument. Reading the expression aloud prevents a common reversal: “requested is a subset of allowed.”
The methods can stop early. A subset test can return false as soon as it finds a missing value, and a disjointness test can return false at the first shared value. The exact traversal also depends on the reported sizes.
The argument follows a three-member protocol
The ECMAScript 2026 GetSetRecord operation reads size, converts it to a number and then an integer, and obtains has and keys. A negative size throws RangeError. Missing or non-callable methods throw TypeError.
An array is iterable, but it does not have this protocol:
const values = new Set([1, 2, 3]);
try {
values.union([3, 4]);
} catch (error) {
console.log(error instanceof TypeError); // true
}
console.log([...values.union(new Set([3, 4]))]); // [1, 2, 3, 4]
The API asks for keys() rather than Symbol.iterator. A generic iterable may be expensive, infinite, or unable to answer has efficiently. The explicit protocol gives the algorithm both membership and traversal operations plus a size hint.
A Map is set-like over its keys
Map has size, has, and keys, so it works without conversion. Its values do not participate.
const selected = new Set(['alpha', 'gamma']);
const records = new Map([
['alpha', { ready: true }],
['beta', { ready: false }],
]);
console.log([...selected.intersection(records)]); // ['alpha']
console.log([...selected.difference(records)]); // ['gamma']
This is useful when a map is the authoritative index and another operation only cares whether keys exist. Converting records.keys() to a temporary Set would allocate storage the map already provides.
Custom adapters must keep their story straight
A custom set-like object should report a size consistent with the values returned by keys() and recognized by has(). The standard uses size to choose which side to traverse for methods such as intersection and isDisjointFrom.
function setLikeFromIndex(index) {
return {
get size() {
return index.size;
},
has(value) {
return index.has(value);
},
keys() {
return index.keys();
},
};
}
const index = new Map([
['north', 3],
['south', 5],
]);
const requested = new Set(['north', 'west']);
console.log([...requested.intersection(setLikeFromIndex(index))]);
// ['north']
The methods retrieve the protocol members at the start, but they call those functions during the operation. Getters, has, and iterators can have side effects. Mutating either collection while a set method runs makes results harder to reason about and can change traversal behavior.
A fake size is more than a performance hint. NaN throws TypeError, a negative value throws RangeError, and positive infinity is allowed. An inaccurate finite size may steer the algorithm down a different branch and expose inconsistencies between has() and keys().
Order follows the operation
Sets preserve insertion order, but each operation has its own source for that order. union starts with every receiver value, then appends new values from the argument’s keys() iterator. difference keeps the receiver’s surviving order.
For intersection, the standard may traverse the smaller side. If the argument reports a smaller size, matching values can follow the argument’s key order instead of the receiver’s order. Treat an intersection as a mathematical membership result unless the exact specified order is part of a tested contract.
Set membership uses SameValueZero. NaN matches itself, and 0 matches -0. Objects match only by identity. A custom set-like object’s has method should follow compatible semantics if callers expect it to behave like a native set.
Use the new methods when the domain operation is set algebra. Convert a plain iterable to new Set(iterable) when it is finite and materialization is acceptable. Build a set-like adapter when an existing index can provide honest size, has, and keys operations without a copy.