How Lodash _.slice Handles String Parameters

This article explains how the Lodash _.slice method processes start and end parameters when they are intentionally passed as string values. Unlike JavaScript's native Array.prototype.slice, which automatically converts string parameters into numbers, Lodash relies on strict type checks that alter how string inputs are resolved, often leading to unexpected results such as returning empty arrays.

Strict Type Checking in Lodash

Lodash implements _.slice as an optimized alternative to the native Array.prototype.slice. Rather than performing standard JavaScript type coercion (such as ToIntegerOrInfinity), Lodash inspects parameter types directly using the typeof operator.

The internal logic determines positional offsets by verifying whether arguments are strictly numbers:

How String Values for start Behave

When you pass a string as the start parameter (e.g., _.slice(array, '2')), the type check typeof start === 'number' evaluates to false.

Because Lodash does not convert string numbers into numerical primitives:

  1. start is reset to 0.
  2. Slicing begins at index 0, ignoring the intended string value.
const items = ['a', 'b', 'c', 'd'];

// Passing a string start:
_.slice(items, '2'); 
// Returns: ['a', 'b', 'c', 'd'] (start falls back to 0)

How String Values for end Behave

The behavior for a string end argument depends on how JavaScript evaluates relational comparisons before the type check occurs.

Internally, Lodash first checks:

end = end > length ? length : end;
  1. When end is less than or equal to length: Evaluating '2' > 4 uses standard relational coercion, resulting in false. Lodash retains end = '2'. It then runs if (typeof end === 'number'). Because '2' is a string, this condition fails, triggering the fallback: end = 0. Since start and end both resolve to 0, _.slice produces an empty array ([]).

  2. When end is greater than length: Evaluating '5' > 4 coerces '5' to a number and returns true. Lodash reassigns end = length. Because length is an integer primitive, typeof end === 'number' now evaluates to true, and end remains bounded to the array's full length.

const items = ['a', 'b', 'c', 'd'];

// String end <= length:
_.slice(items, 0, '2'); 
// Returns: [] (end falls back to 0)

// String end > length:
_.slice(items, 0, '10'); 
// Returns: ['a', 'b', 'c', 'd'] (end is replaced by array length: 4)

Difference from Native Array.prototype.slice

JavaScript's native Array.prototype.slice coerces arguments to integers using standard ECMAScript conversions:

const items = ['a', 'b', 'c', 'd'];

// Native slice coerces strings to numbers:
items.slice('1', '3'); 
// Returns: ['b', 'c']

// Lodash _.slice does not coerce strings:
_.slice(items, '1', '3'); 
// Returns: [] (start becomes 0, end becomes 0)

Handling String Inputs Properly

To avoid unexpected empty arrays or incorrect slices when dealing with string values (such as URL parameters or form inputs), explicitly convert the arguments to numbers before calling _.slice:

const start = Number('1');
const end = Number('3');

_.slice(items, start, end);
// Returns: ['b', 'c']