How to reliably check an object is an EcmaScript 6 Map/Set?
You can use the instanceof
operator:
function isSet(candidate) {
return candidate instanceof Set;
}
If the candidate object has Set.prototype
in its prototype chain, then the instanceof
operator returns true
.
edit — while the instanceof
thing will work most of the time, there are situations in which it won't, as described in Bergi's answer.
The situation is similar to pre-ES5 methods to detect arrays properly and reliably. See this great article for the possible pitfalls of implementing isArray
.
We can use
obj.constructor == Map
/Set
, but that doesn't work on subclass instances (and can easily be deceived)obj instanceof Map
/Set
, but that still doesn't work across realms (and can be deceived by prototype mangling)obj[Symbol.toStringTag] == "Map"
/"Set"
, but that can trivially be deceived again.
To be really sure, we'd need to test whether an object has a [[MapData]]
/[[SetData]]
internal slot. Which is not so easily accessible - it's internal. We can use a hack, though:
function isMap(o) {
try {
Map.prototype.has.call(o); // throws if o is not an object or has no [[MapData]]
return true;
} catch(e) {
return false;
}
}
function isSet(o) {
try {
Set.prototype.has.call(o); // throws if o is not an object or has no [[SetData]]
return true;
} catch(e) {
return false;
}
}
For common use, I'd recommend instanceof
- it's simple, understandable, performant, and works for most reasonable cases. Or you go for duck typing right away and only check whether the object has has
/get
/set
/delete
/add
/delete
methods.
You can simply use:
export function isMap(item) {
return !!item && Object.prototype.toString.call(item) === '[object Map]';
}
export function isSet(item) {
return !!item && Object.prototype.toString.call(item) === '[object Set]';
}
Unless the prototype of this method was overridden