nackjicholson nackjicholson - 1 month ago 12
Javascript Question

How to reliably check an object is an EcmaScript 6 Map/Set?

I just want to check that an object is a

Map
or
Set
and not an
Array
.

to check an Array I'm using lodash's
_.isArray
.

function myFunc(arg) {
if (_.isArray(arg)) {
// doSomethingWithArray(arg)
}

if (isMap(arg)) {
// doSomethingWithMap(arg)
}

if (isSet(arg)) {
// doSomethingWithSet(arg)
}
}


If I were to implement
isMap
/
isSet
, what does it need to look like? I'd like for it to be able to catch subclasses of
Map
/
Set
if possible as well.

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.

Comments