Reputation: 100000
How can I check if a value is Symbol in JS?
I do not see a Symbol.isSymbol(x)
method. My test of (x instanceof Symbol)
does not seem to work either.
Upvotes: 25
Views: 17527
Reputation: 1558
The most simple way of testing for a symbol is indeed:
typeof value=="symbol"
But you may not want to rely on typeof
because it will return "object"
for symbols that have been cast to an object. Such symbols still function as symbols in every way and should not be dismissed.
Symbols that have been polyfilled for pre-ES6 browsers also return "object"
when using typeof
because the return type of "symbol"
was not yet available before ES6.
So if you want to include symbol objects and support pre-ES6 polyfills, you can either use instanceof
:
Object(value) instanceof Symbol;// works in most cases
or check the constructor:
value && value.constructor === Symbol;
The above solutions won't work across global environments but that's easily solvable by comparing the constructor to the string representation of the Symbol function. So here is your best bet. If the constructor is non-nullish, it is coerced to a string:
value && value.constructor == Symbol.toString();
The above won't work if the constructor property is overwritten which is an edge case you probably never need to worry about. But for completeness here is a solution that guarantees success. It uses try/catch which is slow so you might not want to do it this way.
// Annoyingly complex, yet guaranteed to work
function isSymbol(value) {
var isSymbol, type = typeof value;
if (type=="object" && value) try {
Symbol.prototype.toString.call(value)// Throws if value is not a Symbol primitive or object
isSymbol = 1;// This is only set if it didn't throw
}
catch(e) {
}
return type=="symbol" || !!isSymbol;// cast to boolean
}
Upvotes: 1
Reputation: 1191
Updated 2022: Go with the accepted answer! If you're working in an environment so outdated that Symbol
needs to be polyfilled, then you'll know that already. You'll be excruciatingly aware of it. You'll be haunted by it. Then, sure, use my answer. Otherwise don't bother. typeof x === 'symbol'
is almost definitely all you need these days.
In ES 2015 and up, typeof x === 'symbol'
is all that's needed. But it won't work if you're transpiling your code to ES 5.1 or earlier, even if you're using a polyfill for the Symbol
builtin.
Every polyfill I've seen, including the babel-polyfill, implements Symbol as an object (i.e. typeof x === 'object'
) using a constructor function called Symbol
. So in those cases you can check that Object.prototype.toString.call (x) === '[object Symbol]'
*.
Putting it all together, then, we get:
function isSymbol (x) {
return typeof x === 'symbol'
|| typeof x === 'object' && Object.prototype.toString.call (x) === '[object Symbol]';
}
*Note that I'm not using instanceof
in the transpiled scenario. The problem with instanceof
is that it only returns true for objects that were created within the same global context as the assertion being made. So if, say, a web worker passes a symbol back to your page, or symbols are passed between iframes, then x instanceof Symbol
will return false! This has always been true of all object types, including the builtins. instanceof
often works just fine, but if there's any chance of your code being in a "multi-frame" scenario as I've described, use with caution!
Upvotes: 6
Reputation: 497
The most efficient way is to test the constructor of a value:
const result = (value && value.constructor === Symbol);
Upvotes: 0