Lodash Math Coercion Rules: Objects to Primitives

This article explains the specific mechanisms and internal rules the Lodash JavaScript library uses to coerce objects into primitive values across its math functions. You will learn the execution order of object methods like valueOf and toString, how Lodash normalizes derived primitives into numbers, and how specific functions such as _.add, _.subtract, and _.sum handle complex types, arrays, and edge cases without throwing native runtime exceptions.


The Internal Coercion Pipeline

Lodash math operations—including arithmetic methods (_.add, _.subtract, _.multiply, _.divide) and aggregate functions (_.sum, _.mean, _.min, _.max)—process arguments through an internal conversion pipeline primarily governed by baseToNumber and toNumber.

When an object is supplied as an operand to a Lodash math function, it does not immediately enter standard ECMAScript arithmetic evaluation. Instead, Lodash explicitly extracts a primitive value before performing the calculation.

1. Object-to-Primitive Extraction Order

When Lodash encounters an argument where isObject(value) evaluates to true, it follows a strict two-step unwrapping process:

  1. valueOf() Evaluation: Lodash checks whether the object has a callable valueOf method. If typeof value.valueOf === 'function', it invokes it. If the returned value is a primitive (not an object), this value is used for the next conversion step.
  2. Fallback to String Conversion: If valueOf() returns another object (which is the default behavior for plain objects and arrays), Lodash falls back to string coercion using value + ''. This implicitly invokes toString().

Unlike standard modern JavaScript engines, which consult the Symbol.toPrimitive method first, Lodash's legacy-compatible toNumber implementation manually isolates valueOf() and stringification to prevent unhandled prototype exceptions.

2. Primitive-to-Number Normalization

Once an object is unwrapped to a primitive, Lodash normalizes the resulting value into a standard JavaScript number using the following rules:

3. Function-Specific Coercion Behaviors

Lodash math functions handle coerced primitives differently depending on whether the operation is strictly arithmetic or supports concatenation.

_.add (Polymorphic Addition)

_.add uses the internal createMathOperation wrapper with a string fallback:

_.add({ valueOf: () => 10 }, 5); // 15
_.add({ toString: () => 'hello ' }, 'world'); // "hello world"
_.add([10], [20]); // "1020" (Arrays stringify to "10" and "20")

_.subtract, _.multiply, and _.divide (Strict Arithmetic)

These operations enforce numeric coercion on both sides:

_.subtract({ valueOf: () => 50 }, 20); // 30
_.multiply(['5'], ['4']); // 20
_.divide({ valueOf: () => 'invalid' }, 2); // NaN

_.sum, _.mean, and Collection Functions

Aggregation functions iterate over collections and apply numeric coercion per element:

const items = [
  { valueOf: () => 10 },
  { valueOf: () => 20 },
  [30]
];

_.sum(items); // 60
_.mean(items); // 20

Common Object Coercion Results Summary