CodeLit

    CoursesProductsBlogYouTubeJoin our Discord

    CodeLit

    Powered by CourseLit

    Legal

    Terms of usePrivacy policy

    Our Products

    CourseLitMediaLitWebLit
      1. Blog

      2. Type Coercion In Jav...

      Type Coercion In JavaScript: A Deep Dive

      Rajat Saxena

      Rajat Saxena

      ยท September 2, 2025

      Table of Contents

        1. Everything gets converted to either a number or a string ultimately.1.1. How does JavaScript decide if it should convert an operand to a string or a number?2. JavaScript uses a function to convert anything to either number or string2.1. Primitives will further converge to either string or number2.2. Hands-on3. Confusing operators3.1. Addition3.2. Loose Equality

      Type conversion (also known as coercion) in JavaScript confuses a lot of developers and makes a significant chunk of interview questions and general trivia/gotchas. In this post, we will learn the basic building blocks to coercion and see how to apply those to actual real world problems.

      Type coercion is JavaScript's attempt to be fault-tolerant and forgiving. It will try to convert the data types of the variables you are performing an operation upon so that the operation does not error out.

      Let's establish the first building block.

      1. Everything gets converted to either a number or a string ultimately.

      JavaScript will apply a few rule and ultimately all the operands will be converted to either of these values.

      1.1. How does JavaScript decide if it should convert an operand to a string or a number?

      Glad that you asked! There are certain operations which prefer numbers and others which prefer strings. Let's see a few examples.

      1.1.1. Operations that prefer numbers

      *       // multiply
      
      %       // modulo
      
      ++      // prefix (or postfix) operators

      Listing 1

      1.1.2. Operations thats prefer strings

      `${}`         // string templates
      
      String()      // creating an string object

      Listing 2

      Then there are operations which are neutral like, addition and loose equality (==). We will learn more about those in Section 3.

      So how does JavaScript convert any value to number/string? For that, we are going to establish the second building block.

      2. JavaScript uses a function to convert anything to either number or string

      JavaScript uses a function called ToPrimitive which converts any input to a primitive value. If the input is already a primitive, it is returned as is. Otherwise, it is first converted into a primitive value and then returned.

      JavaScript has the following primitives.

      • string

      • number

      • bigint

      • boolean

      • undefined

      • symbol

      • null

      JavaScript calls ToPrimitive function automatically when the type conversion happens. While calling this function, JavaScript also sends the preference i.e. whether it would like to convert that value into a string or a number, as an input. This input is called hint and its value can be string | number|default.

      Following is the over-simplified pseudo-code of ToPrimitive. Please keep in mind I have glossed over non-essential stuff and we will follow the same pattern in all of such examples.

      ToPrimitive (input, hint: string|number|default = default):
          if input is an object:
              if input has a function called toPrimitive:
                  result = toPrimitive(input, hint)
                  if result is not an object:
                      return result
                  else:
                      throw error
      
              if hint == default:
                  hint = number
      
              return OrdinaryToPrimitive(input, hint)
      
          // already a primitive
          return input

      Listing 3

      Here is what the above pseudo-code is doing.

      1. If the hint is not defined then it is initialised to default.

      2. If the input is an object, it will follow Step #3 and beyond. Otherwise, it will return the input as it is already a primitive.

      3. If the input has a method called toPrimitive, it will call that method with the hint.

      4. If the result of Step #3 is an not an object, it will return this result. Otherwise, it will throw an error (TypeError in particular)

      5. If the input does not has a method called toPrimitive, first the hint is set to number and then a function call is made to OrdinaryToPrimitive.

      In most cases, this toPrimitive function is not defined hence the responsibility lands on OrdinaryToPrimitive function to convert an object into a primitive.

      Following is the implementation in over-simplified pseudo-code.

      OrdinaryToPrimitive(hint: string|number):
          if hint == "string"
              methods = ["toString", "valueOf"]
          else
              methods = ["valueOf", "toString"]
      
          loop over methods array:
              if method is callable:
                  result = method()
                  if result is not of object type:
                      return result
                  else
                      throw error

      Listing 4

      So as you can see from above, JavaScript will call two methods i.e. toString and valueOf in a preferred sequence depending upon the hint.

      2.1. Primitives will further converge to either string or number

      We saw that ToPrimitive will return primitive values. These primitive values will be ultimately converted to string/number. JavaScript uses ToNumber and ToString for this. These methods define how values like null, undefined, symbol, bigint etc. are converted to numbers and strings respectively.

      2.2. Hands-on

      Let's see the above concepts in action. For that let's define a variable as follows.

      let b = {
          valueOf: function () { return 1 },
          toString: function () { return 'bee' }
      }

      Listing 5

      Now, if we do the following

      b * 2 // prints 2

      Listing 6

      This is because the multiplication operator prefers the operands to be numbers. Hence JavaScript tried to convert the object b to a number. For this, it invokes ToPrimitive with number hint, which in turn calls OrdinaryToPrimitive with number hint and hence valueOf is invoked.

      Let's try something else.

      `${b}` // prints "bee"

      Listing 7

      Again, using the above logic and section 1.1.2, toString gets invoked and hence the result.

      3. Confusing operators

      There are operators which are neutral i.e. to don't prefer string over numbers and vice-versa. The following are a few such operators.

      1. Addition (+)

      2. Loose equality (==)

      3. Less than (<)

      This class of operators leads to the most confusion around coercion in JavaScript. We will see how addition and loose equality works in this post. Less than is somewhat more complex than the other two so we will leave that out for now.

      3.1. Addition

      Again, let's look at the pseudo-code of how this is implemented.

      Addition(x, y):
          convert operands to primitives
          if any operand is string:
              convert both operands to string
              concatenate the operands
              return result
          else:
              convert all operands to numbers
              if any operand is unconvertible (symbol, bigint):
                  throw error
              else
                  add both operands
                  return result

      Listing 8

      This one is simple.

      1. If any operand is a string, convert both to string, concatenate both and return the result.

      2. Otherwise, convert both to numbers.

      3. If there is an error in converting to numbers, throw error. Otherwise, add both and return the result.

      3.1.1. Hands-on

      Let's the try the easier ones first.

      'e' + 1 // prints 'e1'
      1 + 2   // prints 3

      Listing 9

      Now, let's try a complex one.

      let b = {
          valueOf: function () { return 1 },
          toString: function () { return 'bee' }
      }
      
      b + 1 // prints 2

      Listing 10

      Here, one operand i.e. b is an object and the other one is a number. JS will try to convert both of these to primitive (see Listing 8, line 2). To convert b to primitive, ToPrimitive will be invoked with the default hint. We know the if the hint is default, it is overridden with number (see Listing 3, line 10). Hence b will be coerced to a number. So, valueOf is invoked which returned 1.

      Now, let's try adding b to a string

      b + '1' // prints "11"

      Listing 11

      Since '1' is a string, hence b will be coerced to a string (see Listing 8, lines 3-4). Hence, toString will be invoked from b.

      One more

      true + false // prints 1

      Listing 12

      Here, both of the operands will be converted to numbers using ToNumber (see Listing 8, line 8) and hence the result.

      3.2. Loose Equality

      Brace yourself, this operation's pseudo-code is considerably trickier. The values x' and y' denote the values after the conversion.

      LooseEquality(x, y):
          If both operands are of same types, do strict comparison (===)
      
          if comparing null and undefined:
              return true
      
          if comparing number and string:
              convert the string operand to number
              LooseEquality(x', y')
      
          if comparing bigint and string:
              convert the string operand to bigint
              if the above results in NaN:
                  return false
              LooseEquality(x', y')
      
          if any operand is boolean:
              convert boolean operands to number
              LooseEquality(x', y')
      
          if one operand is object and the other is one of [string, number, bigint, symbol]:
              convert the object to primitive value
              LooseEquality(x', y')
      
          if comparing number and bigint:
              if any operand is non-finite i.e. NaN, +Infinity, -Infinity:
                  return false
              if the mathematical values of both operands are the same:
                  return true
              else:
                  return false
      
          return false

      Listing 13

      Here are some easily deduced take-aways from the above algorithm.

      1. If the types are same, Strict equality operator (===) is used.

      2. Comparing null to undefined will result in true.

      3. While comparing a string to a number, the string operand is converted to a number.

      4. Boolean values are converted to numbers.

      5. Objects are converted to primitive values.

      3.2.1. Hands-on

      3.2.1.1. Let's try this.

      1 == '1' // prints true

      Listing 14

      The string operand '1' gets converted to 1 (see Listing 13, line 8) and the equality operator is invoked again with arguments (1, 1). Due to Listing 13, line 2, we get true.

      3.2.1.2.

      let d = {
          valueOf: function () { return 0 },
          toString: function () { return 'bee' }
      }
      
      d == 0 // prints true

      Here, d will be converted to a primitive value (see Listing 13, line 21-22). ToPrimitive will be called with the default hint which means that the it will be coerced to a number (see Listing 3, line 10). Hence, we get 0 == 0 which is true.

      3.2.1.3.

      [] == 0     // prints true
      [] == false // prints true

      Arrays are objects so [] gets converted to 0 (Listing 13, line 21-22). Hence the results.

      That's it! If you have questions, come discuss this over Twitter or in my Discord server.