When I see someone calling someVar.toString()
, I usually advise them to use String(someVar)
1 instead, as it handles null
and undefined
cases gracefully (depending on the definition of gracefully, of course) instead of throwing2.
For the longest time I was under the impression that calling String(someVar)
would never throw, for any value of someVar
. However, I was wrong. There are two cases where it also throws:
- When passed an object whose
Symbol.toPrimitive
exists but isn’t callable or calling it with'string'
as first argument returns a non-primitive or throws. - When passed an object that doesn’t have a
Symbol.toPrimitive
property andtoString
orvalueOf
each either don’t exist, aren’t callable, return a non-primitive, or throw.
Most likely you’ll encounter the case where none of these methods are declared when handling an object that doesn’t have a prototype, e.g. if it was created using Object.create(null)
. In that case, my advice wouldn’t have been better or worse because both approaches throw.
But we can also craft an object specifically to throw when passed to String
but not when calling toString
on it:
In this case, my initial advice would be completely wrong and toString
would have been the better option.
Of course, we could also easily have crafted the opposite:
I stand by the stance that String(someVar)
is usually the better approach in real-world scenarios (it also looks nicer) but I’ve come around to thinking that being explicit about null
and undefined
is better than being implicit so I always advice for having null checks anyway.
Investigating this also helped me clear up another misconception. The String
construction (when used as a function) doesn’t just apply the abstract operation ToString
as defined by ECMAScript.
ToString
is specified as follows:
But I already knew that String(Symbol('a'))
doesn’t throw, so something else must be going on there.
The specification of String(value)
clarified things:
That’s it, symbols are special-cased in String(value)
, they won’t ever throw!
But symbols will throw when used in an untagged template string literal as there, the ToString
operation is used.
Top comments (0)