How Lodash _.add Handles String Concatenation
This article examines how the Lodash library processes numerical
strings in its _.add method. It explores the internal
architecture behind createMathOperation, details how
type-checking dictates string concatenation versus numeric addition, and
explains why Lodash preserves standard JavaScript addition semantics for
string primitives while applying explicit conversions like
baseToNumber for non-primitive inputs.
The Architecture of
_.add
In Lodash, _.add is not an isolated function containing
manual mathematical logic. Instead, it is generated using an internal
higher-order factory function called
createMathOperation:
const add = createMathOperation((augend, addend) => augend + addend, 0);When you invoke _.add(augend, addend), the call is
routed through createMathOperation. This wrapper
standardizes input handling, default fallbacks, and type coercion across
mathematical methods like add, subtract,
multiply, and divide.
How Type Resolution Operates
The internal implementation of createMathOperation
determines whether to treat arguments as strings or numbers before
invoking the mathematical operator. The core branching logic functions
as follows:
if (typeof value === 'string' || typeof other === 'string') {
value = baseToString(value);
other = baseToString(other);
} else {
value = baseToNumber(value);
other = baseToNumber(other);
}
return operator(value, other);Because of this conditional check:
- Primitive Numerical Strings Are Concatenated: If
either argument has a primitive
typeofvalue of'string', Lodash intentionally invokesbaseToStringon both arguments. When passed to(augend, addend) => augend + addend, JavaScript performs standard string concatenation. For example,_.add('20', '5')produces'205', not25. Lodash deliberately mirrors native JavaScript+semantics rather than forcibly coercing string primitives into numbers. - Non-String Values Route to
baseToNumber: If neither argument evaluates to'string'viatypeof, execution branches tobaseToNumber. This includes primitives like numbers, booleans, andnull, as well as boxed object types.
Preventing Concatenation for Non-Primitive String Objects
While primitive strings are concatenated, Lodash prevents
concatenation when numerical strings are wrapped as objects (e.g.,
Object('20') or new String('20')).
In JavaScript:
Object('20') + Object('5'); // Evaluates to "205" natively due to ToPrimitive conversionIn Lodash:
- The check
typeof value === 'string'evaluates tofalsebecause the type of a boxed string object is'object'. - Both arguments bypass the
baseToStringbranch and enter theelsebranch. - Each argument is processed by
baseToNumber, which extracts the primitive numeric value usingvalueOf()or numeric parsing. - The underlying addition operator receives two numeric primitives:
operator(20, 5). - The result is
25, effectively circumventing native string concatenation.
Safe Coercion with
baseToNumber
When inputs enter the numeric branch, Lodash avoids raw JavaScript
coercion (such as +val or Number(val)) to
prevent runtime exceptions. The internal baseToNumber
helper handles edge cases safely:
- Symbol Handling: Directly coercing a
Symbolin JavaScript via+throws aTypeError.baseToNumberidentifies symbols viaisSymboland safely returnsNaN. - Object Values: If an object with a custom
valueOfmethod is provided,baseToNumbernormalizes it before evaluating whether it can be parsed as a float or integer. - Whitespace and Signs: It sanitizes string representations of hexadecimal, binary, and octal notations to prevent unexpected parsing failures.
Summary of Behavior
Lodash's _.add avoids unexpected concatenation
exclusively through strict typeof boundary checks:
- Primitive strings (
"1" + "2"): Lodash does not prevent concatenation; it explicitly embraces it viabaseToString. - Boxed string objects or non-string types: Lodash
prevents concatenation by redirecting the values through
baseToNumber, ensuring that binary addition operates purely on evaluated numeric types.