Lodash _.size Behavior with Custom Length Getter

When evaluating an object with a custom .length getter in Lodash, _.size will return the exact value produced by that getter, provided the returned value meets Lodash’s definition of a valid array-like length. If the custom getter returns an invalid length (such as a negative number, a non-integer, or a non-numeric type), Lodash bypasses the getter and instead falls back to returning the total count of the object’s enumerable own string-keyed properties.

The Internal Mechanics of _.size

To understand why this happens, look at how Lodash determines the size of an incoming collection:

  1. Null/Undefined Check: If the target is null or undefined, 0 is returned immediately.
  2. Array-Like Validation (isArrayLike): Lodash checks if the value is array-like by checking two primary conditions:
    • The value is not null and is not a function.
    • The value possesses a valid length property, validated through the internal isLength(value.length) function.
  3. Dispatch:
    • If isArrayLike evaluates to true, Lodash directly accesses and returns collection.length (with a special case for strings to handle Unicode symbols properly).
    • If isArrayLike evaluates to false, Lodash checks if the object is a Map or Set to return their .size. Otherwise, it falls through to base object handling, returning Object.keys(collection).length.

Definition of a Valid Length

For a custom .length getter to trigger the array-like branch, the value it returns must satisfy isLength:

Behavior Examples

1. Valid Length Returned by Getter

const obj = {
  a: 1,
  b: 2,
  get length() {
    return 42;
  }
};

_.size(obj); // Returns: 42

Because 42 is a safe, non-negative integer, isArrayLike(obj) resolves to true. Lodash treats the object as array-like and returns obj.length, which is 42, completely ignoring the fact that the object only has two own enumerable data properties (a and b).

2. Invalid Length Returned by Getter

const obj = {
  a: 1,
  b: 2,
  get length() {
    return -5; // Negative, not a valid length
  }
};

_.size(obj); // Returns: 3

Because -5 fails the isLength check, isArrayLike(obj) resolves to false. Lodash treats obj as a plain object and counts its enumerable own properties. Because getter properties defined on object literals are enumerable by default, the keys counted are ['a', 'b', 'length'], resulting in a return value of 3.

Getter Invocation Side Effect

Because of how _.size and isArrayLike are structured, custom getters are invoked more than once during a single call:

  1. First invocation occurs during the isLength(collection.length) check inside isArrayLike.
  2. Second invocation occurs when collection.length is read to return the final size value.

If the custom getter performs side effects or returns dynamic numbers, it will be executed twice, and the returned size will reflect the value from the second read.