A simple template literal expression:
`${value}`
… will throw TypeError
in at least two cases:
value
is a Symbol;try {
`${Symbol('nope')}`
} catch (error) {
console.error(`${error.name}: ${error.message}`)
}
value
is an empty non-inherited object.try {
`${Object.create(null)}`
} catch (error) {
console.error(`${error.name}: ${error.message}`) // weird error message in this case: "No default value"
}
What are the other cases in which it throws an error, if any? Is there some universal rule for that (like, not having some method used internally)?
What are the other cases in which it throws an error, if any?
There's too many to count. Basically every object for which coercing it to a (string) primitive fails.
Is there some universal rule for that (like, not having some method used internally)?
Not a single method but three of them. The abstract ToString operation which is used in template literal evaluation (and everywhere else in the spec when a value needs to be coerced to a string - in particular for string concatenation, values to be used as property keys, and arguments that are expected to be strings) does
throw on symbols, as you noted
call the abstract ToPrimitive operation on objects, which in turn tries to call either
o[Symbol.toPrimitive]("string")
o.toString()
o.valueOf()
if the method exists. If toString
returns an object, valueOf
is tried as well.
An exception is thrown if the called methods throw, or if the properties exist but have a value that is neither undefined
, null
, nor a function, or if none of the methods exists, or if none of the methods returned a returned a primitive (non-object) value. If the method returned a primitive value, it is fed to ToString again (which throws on symbols and returns a string otherwise).