Symbol.prototype.toString()

Formats a symbol as the string "Symbol(description)" for logging and debugging.

Signature
toString()
  • Added in JavaScript ES2015

Return value

stringA string of the form "Symbol(description)", where description is the symbol's description (empty when it has none).

Errors

Throws a TypeError if this is not a symbol primitive or Symbol wrapper object.

Symbol.prototype.toString() overrides Object.prototype.toString() and returns the familiar "Symbol(id)" form. It is one of the two supported ways to turn a symbol into text; the other is String(sym), which special-cases symbols and delegates here.

When to reach for it

Call it explicitly any time a symbol has to appear in text — a log line, an error message, a key in a debug dump. Symbols deliberately refuse implicit string conversion: "key: " + sym and `${sym}` both throw a TypeError. That rule exists so a symbol never silently degrades into a string property key. Making the conversion explicit is the point, not an inconvenience.

What trips people up

String(sym) works but sym + "" throws, and the difference catches people out inside template literals and array join() calls. Beware too that String(sym) is safe while new String(sym) is not. And console.log(sym) may print a symbol without complaint because the host formats it — that is not string coercion, so code that works in the console can still throw once the value flows into real string concatenation.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Include a symbol key in an error message or log line
  • Build a debug dump of Reflect.ownKeys() where symbols and strings must all render
  • Compare the printed form of a well-known symbol while diagnosing protocol lookups

Frequently asked questions

Why do I get "Cannot convert a Symbol value to a string"?

Because you used implicit coercion such as `${sym}`, sym + '', or join(). Call sym.toString() or String(sym) instead, which are the explicit conversions the language allows.

Does String(symbol) work?

Yes. String() special-cases symbols and returns "Symbol(description)" without throwing, unlike concatenation or template literals.

What does toString() return for a symbol with no description?

It returns the string "Symbol()" with nothing between the parentheses, while the description property for that same symbol is undefined.

More examples

Rendering mixed string and symbol keys

String(k) handles both the string key and the symbol key, so the map never throws. Interpolating k directly would fail on the symbol.

Example · javascript
const TAG = Symbol("tag");
const obj = { a: 1, [TAG]: 2 };

const rendered = Reflect.ownKeys(obj).map(
  (k) => `${String(k)}=${obj[k]}`,
);
console.log(rendered);
console.log(rendered.join(", "));

Template literals still throw

The first three all perform implicit string conversion and throw TypeError — including Array.prototype.join() — while only the explicit String(s) call succeeds.

Example · javascript
const s = Symbol("boom");
const attempts = [
  () => `${s}`,
  () => s + "",
  () => [s].join(","),
  () => String(s),
];

for (const fn of attempts) {
  try {
    console.log("ok:", fn());
  } catch (err) {
    console.log("threw:", err.constructor.name);
  }
}

Discussion

  • Be the first to comment on this lesson.