Customize objects coercion in JavaScript
Tags: javascript, node, coercion, objects
Published: 2019-03-24 10:53:33

---
title: Customize objects coercion in JavaScript
published: true
description: Did you know that Javascript allows us to customize the result of coercion when it is performed on an object defined by us?
tags: javascript, node, coercion, objects
---

![](https://thepracticaldev.s3.amazonaws.com/i/q4ocbx6ttxp4bxh7tm6n.jpg)

## Introduction

JavaScript, unlike other programming languages, doesn't allow operators overloading. What it allows us to do is to modify the primitive value in which an object is transformed. This is because when an object is coerced, the result is, and must be, a primitive value.

Unfortunately we can consider this opportunity only a paltry consolation prize, because the control we can obtain is quite limited. In any case it could always be useful to know what possibilities the language offers to us, considering that ES6 has brought a solution of considerable value.

The whole article is based on a good number of concepts related to [coercion](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch4.md). In fact, what we can do is modify the result of the coercion performed on our objects.





## Customization before ES6

Since coercion of objects is strongly based on __toString__ and __valueOf__ methods, which are callable by default on any object, what we could do before ES6 was simply redefine those methods on our objects.

Why did I use the word _redefine_? Why are the two methods callable by default? In Javascript all objects are automatically linked to the __Object.prototype__ object thanks to what is called the _prototype chain_.
This object defines a fair number of methods which are therefore invocable directly from the all the objects connected to it. Learn more [here](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch5.md).

Before to redefine their behaviour, let's see the default one:
```js
    var object = {
        prop: "value",
    };

    object.valueOf(); // object
    object.toString(); // "[object Object]"
```
As you can see, __valueOf__ simply returns the object itself (a reference to). Instead the other method, __toString__, reads the value of the __[[Class]]__ internal property and constructs the well know _"[object Object]"_ string.

Also arrays are objects, but for them the behaviour of the __toString__ method was already redefined by default:
```js
    var array = [1, 2, 3];

    array.valueOf(); // array
    array.toString(); // "1,2,3"
```

When any object is coerced, depending on the initial conditions, a method between __toString__ and __valueOf__ will take precedence. Only if it does not return a primitive value, such as the valueOf method of Object.prototype, the other method will be invoked as fallback. If even it doesn't return a primitive value, a __TypeError__ will be raised.

__Warning!__ We could decide to return __null__ or __undefined__ when we redefine these methods because they are valid primitive values. However, Javascript never leads to this result for the objects it makes available to us and we should follow its example, returning one of __string__, __number__ and __boolean__.

Numerical operations like `Number(obj)`, `+obj` (__unary +__), __unary -__, __binary -__, __*__, __**__, __/__, __%__ will clearly prioritize the __valueOf__ method, while an explicit coercion operation like `String(obj)` will give priority to the __toString__ method.
Doubts arise when facing the __binary +__ and the __== loose equality__ operator, which give priority to __valueOf__.

Let's see, in practice, how this stuff can help us to reach our goal:
```js
    var myObj = {
        toString: function() {
            return "myObj";
        },
        valueOf: function() {
            return 10;
        }
    }

    // valueOf precedence
    Number(myObj); // 10
    +myObj; // 10
    myObj - 7; // 3
    myObj % 3; // 1
    myObj + 15; // 25
    "this is " + myObj; // "this is 10"
    myObj == 10; // true
        
    // toString precedence
    String(myObj); // "myObj"
```
We have therefore redefined the behaviour of our object.

We may however not be satisfied with the result of `"this is" + myObj;`, preferring `"this is myObj"` to it. Unfortunately, apart from explicit coercion using the __String__ function, the only way to do this, in ES5, is to impose the return of the string `"myObj"` to the __valueOf__ method as well, sacrificing the possibility of using the object in arithmetic operations, penalty an infinite series of __NaN__ deriving from the coercion attempt of the `"myObj"` string in a number.


## Customization with ES6

Let me introduce a new concept: the __hint__. When any object is coerced into a primitive, the decision to give priority to one of the two method of which we spoke earlier is done thanks to an hint.
Possible values for the hint are: __number__, __string__, __default__.
When the hint value is __string__ will be given precedence to the __toString__ method, when it is __number__ or __default__ is the __valueOf__ method which has priority. The only exception to this logic is the __Date__ "type", for which the __default__ hint value will give priority to the __toString__ method.

Let's see what hints are forwarded by the main operations seen so far:

|   Hint  | Operations                                         |
--- | ---
|  string | String(), interpolation [ES6]                      |
|  number | Number(), unary +, unary and binary -, *, **, /, % |
| default | binary +, ==                                       |

It may seem the opposite, but ES5 is also based on the same concepts. The difference lies in the possibility, offered by ES6, to interact directly with this mechanism.

It is necessary to provide to the object a method with a special name, which takes a string with the value of the __hint__. Based on it we can decide what to do, like manually call the __valueOf__ and __toString__ methods if it is our wish, or invoke other methods. The important thing is to produce a primitive value as the end result, to avoid running into a __TypeError__.

What is the special name of this method? It's __Symbol.toPrimitive__. This article will not talk about symbols, because it is not necessary to have confidence with them to reach our goal.

Let's see a possible implementation of it, which allows us to obtain the same behavior defined by ES5:
```js
    let obj = {
        [Symbol.toPrimitive](hint) {
            // it checks if a value is an object
            const isObject = (value) => value !== null 
                                        && typeof value === 'object' 
                                        || typeof value === 'function';

            switch(hint) {
                case "default": 
                case "number": 
                    // precedence to valueOf if the hint is "number" or "default"
                    const value = this.valueOf();

                    // if the result is a primitive, it can be returned
                    if(!isObject(value)) return value;

                    // otherwise the toString method is used as fallback
                    else return this.toString();
                       
                case "string": 
                    // precedence to toString if the hint is "string"
                    const string = this.toString();

                    // if the result is a primitive, it can be returned
                    if(!isObject(string)) return string;
                    
                    // otherwise the valueOf method is used as fallback
                    else return this.valueOf() 
             }
        }
    }
```

What could we do if __toString__ and __valueOf__ methods are not trustable and could lead to a __TypeError__?
Let's see a possible workaround:
```js
    let obj = {
        [Symbol.toPrimitive](hint) {
            // it checks if a value is an object
            const isObject = (value) => value !== null 
                                        && typeof value === 'object' 
                                        || typeof value === 'function';

            switch(hint) {
                case "default": 
                case "number": 
                    // precedence to valueOf if the hint is "number" or "default"
                    let res = this.valueOf();

                    // if the result is a primitive, it can be returned
                    if(!isObject(res)) return res;

                    // otherwise the toString method is used as fallback
                    res = this.toString();

                    // if the result is a primitive, it can be returned
                    if(!isObject(res)) return res;

                    // otherwise returns an appropriate primitive value
                    return NaN;

                       
                case "string": 
                    // precedence to toString if the hint is "string"
                    let res = this.toString();

                    // if the result is a primitive, it can be returned
                    if(!isObject(string)) return string;
                    
                    // otherwise the valueOf method is used as fallback
                    res =  this.valueOf();

                    // if the result is a primitive, it can be returned
                    if(!isObject(res)) return res;

                    // otherwise returns an appropriate primitive value
                    return "";
             }
        }
    }
```

We can easily add more substantial changes to the mechanism, bearing in mind the table shown above. As a practical example, I take the previously defined _myObj_ object, where I wanted the object to be transformed into a string value instead of a numeric one when the __binary +__ operator comes to action. As the table illustrates, the __== operator__ will also be affected by my change.
```js
    let myObj = {
        toString() {
            return "myObj";
        },
        valueOf() {
            return 10;
        },
        [Symbol.toPrimitive](hint) {
            switch(hint) {
                case "number": 
                    return this.valueOf();
                
                case "default":     
                case "string": 
                    return this.toString();
             }
        }
    }
```
It is worth noting that we could still use the numeric value of _myObj_ in a __binary +__ operation or with the __==__ operator if necessary, thanks to the __unary +__.
The table confirms this statement: __unary +__ suggests _"number"_.
```js
    "this is " + myObj; // "this is myObj"
    `greetings from ${myObj}`; // "greetings from myObj"
    "myObj" == myObj; // true

    32 + +myObj; // 42
    `greetings from ${+myObj}`; // "greetings from 10"
    10 == +myObj; // true
```