2.2 Operations that help implement coercion in the ECMAScript specification

The following sections describe the most important internal functions used by the ECMAScript specification to convert actual parameters to expected types.

For example, in TypeScript, we would write:

  1. function isNaN(number: number) {
  2. // ···
  3. }

In the specification, this looks as follows (translated to JavaScript, so that it is easier to understand):

  1. function isNaN(number) {
  2. let num = ToNumber(number);
  3. // ···
  4. }

2.2.1 Converting to primitive types and objects

Whenever primitive types or objects are expected, the following conversion functions are used:

  • ToBoolean()
  • ToNumber()
  • ToBigInt()
  • ToString()
  • ToObject()

These internal functions have analogs in JavaScript that are very similar:

  1. > Boolean(0)
  2. false
  3. > Boolean(1)
  4. true
  5. > Number('123')
  6. 123

After the introduction of bigints, which exists alongside numbers, the specification often uses ToNumeric() where it previously used ToNumber(). Read on for more information.

2.2.2 Converting to numeric types

At the moment, JavaScript has two built-in numeric types: number and bigint.

  • ToNumeric() returns a numeric value num. Its callers usually invoke a method mthd of the specification type of num:

    1. Type(num)::mthd(···)

    Among others, the following operations use ToNumeric:

    • Prefix and postfix ++ operator
    • * operator
  • ToInteger(x) is used whenever a number without a fraction is expected. The range of the result is often restricted further afterwards.

    • It calls ToNumber(x) and removes the fraction (similar to Math.trunc()).
    • Operations that use ToInteger():
      • Number.prototype.toString(radix?)
      • String.prototype.codePointAt(pos)
      • Array.prototype.slice(start, end)
      • Etc.
  • ToInt32(), ToUint32() coerce numbers to 32-bit integers and are used by bitwise operators (see tbl. 1).

    • ToInt32(): signed, range [−231, 231−1] (limits are included)
    • ToUint32(): unsigned (hence the U), range [0, 232−1] (limits are included)
Table 1: Coercion of the operands of bitwise number operators (BigInt operators don’t limit the number of bits).
OperatorLeft operandRight operandresult type
<<ToInt32()ToUint32()Int32
signed >>ToInt32()ToUint32()Int32
unsigned >>>ToInt32()ToUint32()Uint32
&, ^, |ToInt32()ToUint32()Int32
~ToInt32()Int32

2.2.3 Converting to property keys

ToPropertyKey() returns a string or a symbol and is used by:

  • The bracket operator []
  • Computed property keys in object literals
  • The left-hand side of the in operator
  • Object.defineProperty(_, P, _)
  • Object.fromEntries()
  • Object.getOwnPropertyDescriptor()
  • Object.prototype.hasOwnProperty()
  • Object.prototype.propertyIsEnumerable()
  • Several methods of Reflect

2.2.4 Converting to Array indices

  • ToLength() is used (directly) mainly for string indices.
    • Helper function for ToIndex()
    • Range of result l: 0 ≤ l ≤ 253−1
  • ToIndex() is used for Typed Array indices.
    • Main difference with ToLength(): throws an exception if argument is out of range.
    • Range of result i: 0 ≤ i ≤ 253−1
  • ToUint32() is used for Array indices.
    • Range of result i: 0 ≤ i < 232−1 (the upper limit is excluded, to leave room for the .length)

2.2.5 Converting to Typed Array elements

When we set the value of a Typed Array element, one of the following conversion functions is used:

  • ToInt8()
  • ToUint8()
  • ToUint8Clamp()
  • ToInt16()
  • ToUint16()
  • ToInt32()
  • ToUint32()
  • ToBigInt64()
  • ToBigUint64()