What Is Meant by "[@@toPrimitive]()" in JavaScript?

The double at sign (@@) represents a "well-known symbol". The "[@@toPrimitive]()" well-known symbol (introduced in ES6) denotes "Symbol.toPrimitive()", which is used for specifying a class/object method that returns a primitive representation of an object. The Symbol.toPrimitive() method is called with a string argument "hint", which is equal to one of the following values:

  1. "number": suggests the method was called as a result of a numeric coercion algorithm;
  2. "string": suggests the method was called as a result of a string coercion algorithm;
  3. "default": suggests the method was called as a result of a primitive coercion algorithm.

"[@@toPrimitive]()" has great browser support, so you can implement it in your code with ease of mind.

A "Symbol.toPrimitive()" method is the first object method that's called by all type coercion algorithms. Therefore, it can be used to convert an object to a primitive value.

For example, you can implement it in an object literal like so:

// ES6+
const obj = {
    [Symbol.toPrimitive](hint) {
        if (hint === 'number') {
            return 1234;
        } else if (hint === 'string') {
            return 'foo';
        }

        return 'default';
    }
}

console.log(Number(obj)); // 1234
console.log(+obj); // 1234

console.log(String(obj)); // 'foo'
console.log(`${obj}`); // 'foo'

console.log(obj + ''); // 'default'

Similarly, you can implement it as a class method in the following way:

// ES6+
class Foo {
    [Symbol.toPrimitive](hint) {
        if (hint === 'number') {
            return 1234;
        } else if (hint === 'string') {
            return 'foo';
        }

        return 'default';
    }
}

const foo = new Foo();

console.log(Number(foo)); // 1234
console.log(+foo); // 1234

console.log(String(foo)); // 'foo'
console.log(`${foo}`); // 'foo'

console.log(foo + ''); // 'default'

Using the "hint" parameter is not required. Therefore, you may implement a "Symbol.toPrimitive()" method without using it:

// ES6+
const obj = {
    [Symbol.toPrimitive]() {
        return 'foo';
    }
}

console.log(Number(obj)); // NaN
console.log(+obj); // NaN

console.log(String(obj)); // 'foo'
console.log(`${obj}`); // 'foo'

console.log(obj + ''); // 'foo'

The reason coercion to number results in NaN (in the example above) is because the resulting value from Symbol.toPrimitive() method is a non-numeric value. On the flip side, if you return a number for string coercion, it will automatically coerce it to a numeric string:

// ES6+
const obj = {
    [Symbol.toPrimitive]() {
        return 1234;
    }
}

console.log(Number(obj)); // 1234
console.log(+obj); // 1234

console.log(String(obj)); // '1234'
console.log(`${obj}`); // '1234'

console.log(obj + ''); // '1234'

The "Symbol.toPrimitive()" method must always return a primitive or a TypeError is thrown.

Below is an example of "Symbol.toPrimitive()" method returning a non-primitive value, which results in a TypeError being thrown:

const obj = {
    [Symbol.toPrimitive]() {
        return {};
    }
}

// TypeError: Cannot convert object to primitive value
console.log(+obj);

This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.