Write code for mul(2)(3)(4) = 24.
Currying via closures: each call returns a function that captures the running product. The trick is the function must also coerce to its value — implement valueOf/toString so mul(2)(3)(4) evaluates to 24 when used as a number.
mul(2)(3)(4) === 24 is a currying + closure problem with a twist: the result of each call is a function (so it can be called again), but it must also be usable as the number.
The naive version (fixed arity)
If it's always exactly three calls:
const mul = (a) => (b) => (c) => a * b * c;
mul(2)(3)(4); // 24Clean, but mul(2)(3) would be a function, not 6 — and it only handles 3 args.
The real ask: infinite chaining — mul(2)(3)(4)...
Each call returns a function carrying the running product. The key trick: that function needs to coerce to its numeric value when compared or used as a number, via valueOf (or toString):
function mul(a) {
function next(b) {
return mul(a * b); // return another chainable function
}
next.valueOf = () => a; // coercion: makes it usable as the number
next.toString = () => String(a);
return next;
}
mul(2)(3)(4) == 24; // true — == triggers valueOf
+mul(2)(3)(4); // 24 — unary + triggers valueOf- Every
mul(x)returnsnext, which is callable (chain continues) and has avalueOfso JS coerces it to the accumulated product in numeric contexts. - Note:
===won't coerce —mul(2)(3)(4) === 24isfalsebecause it's strictly a function. Use==,+x, or template/string context. Interviewers usually accept==/ explain this nuance.
What this tests
- Closures — the running product captured across calls.
- Currying / function returning function.
- Type coercion —
valueOf/toStringand when JS invokes them. Knowing===won't coerce is the senior detail.
Variation: sum that works with any number of calls
Same pattern: add(1)(2)(3) with valueOf accumulating. The technique generalizes to any "chainable accumulator."
Follow-up questions
- •Why does the chained function need valueOf/toString?
- •Why does === fail but == works?
- •How would you write a fixed-arity curry instead?
- •How would you generalize this to add(1)(2)(3)...?
Common mistakes
- •Returning the product directly, so the result isn't chainable.
- •Forgetting valueOf/toString, so it can't be used as a number.
- •Expecting === to work (it doesn't coerce).
- •Hardcoding three levels instead of supporting arbitrary chaining.
Performance considerations
- •Each call allocates a closure; fine for interview-scale chains. Not a real-world pattern — it's a JS-mechanics exercise.
Edge cases
- •mul(2) used alone — should coerce to 2.
- •Strict equality (===) won't coerce — explain the limitation.
- •Zero in the chain → product is 0.
Real-world examples
- •Demonstrates the closure/coercion mechanics behind currying utilities (lodash.curry) and chainable builder APIs.