Implement currying for infinite sum: sum(10)(20)(30)() === 60
Return a function that either accepts another argument and returns itself, or returns the running total when called with no argument. Implementation hinges on closure + a base case.
This question — "implement sum(10)(20)(30)() === 60" or its sibling mul(2)(3)(4) === 24 — is one of the most common JavaScript closure/currying interview prompts. The interviewer is checking three things: (1) do you understand closures, (2) can you design a function that has two distinct behaviors depending on how it's called, (3) can you discuss the trade-offs of multiple solutions. There are two canonical patterns and one "showy" advanced one.
What "currying" actually means. Currying decomposes a function of N arguments into a chain of N functions of 1 argument each: f(a, b, c) → f(a)(b)(c). Each call captures one argument in a closure and returns a new function waiting for the next. Infinite currying extends this idea to a variable number of calls, with a termination signal.
Approach 1: terminate with an empty call ().
The returned function is dual-purpose: called with an argument, it accumulates and returns itself; called without an argument, it returns the running total.
function sum(a: number) {
let total = a;
function next(b?: number): number | typeof next {
if (b === undefined) return total;
total += b;
return next;
}
return next;
}
sum(10)(20)(30)(); // 60
sum(1)(2)(3)(4)(5)(); // 15Why it works. Every invocation of sum creates a new scope with its own total variable. The next function closes over that total, so successive calls mutate the same binding. The closure is what makes "infinite" currying possible — there's no array, no global, just one captured variable per chain.
Approach 2: valueOf / toString coercion (no terminator).
You can avoid the trailing () by returning a function object whose valueOf returns the running total. When JavaScript needs to coerce the function to a primitive (e.g., + arithmetic, template literal interpolation, console.log), it calls valueOf.
function sum(a: number): any {
const next = (b: number) => sum(a + b);
next.valueOf = () => a;
return next;
}
sum(10)(20)(30) + 0; // 60 — coerced via valueOf
\`\${sum(1)(2)(3)}\`; // "6"This is "slick but surprising" — it relies on implicit coercion, which TypeScript can't type cleanly, and the function's identity is hard to inspect in a debugger. Mention it as a senior flourish; don't use it in production.
Approach 3: fixed-arity currying.
If the question is "given add(a, b, c), write curry(add) so curry(add)(1)(2)(3) === 6," you can write a generic helper:
function curry<T extends (...args: any[]) => any>(fn: T) {
return function curried(...args: any[]): any {
if (args.length >= fn.length) return fn(...args);
return (...rest: any[]) => curried(...args, ...rest);
};
}
const add = (a: number, b: number, c: number) => a + b + c;
curry(add)(1)(2)(3); // 6
curry(add)(1, 2)(3); // 6 — flexible argument grouping
curry(add)(1)(2, 3); // 6This relies on fn.length (the declared arity) as the termination condition. Doesn't work for variadic functions.
For mul(2)(3)(4) === 24 specifically (the file-4 phrasing), the answer is identical, swapping + for *:
function mul(a: number) {
return function next(b: number) {
return mul(a * b);
} as ((b: number) => any) & { valueOf: () => number };
}
// To make it terminate: add valueOf or use the empty-call pattern.Talking points (the senior signal):
- Closure — each chain has its own
total. The function "remembers" because the engine keeps the environment record alive whilenextis reachable. - Function-as-first-class-value — you're returning functions from functions, an idiomatic FP move.
- Memory — each call allocates a closure; not free for very long chains. If you needed billions of items, prefer
array.reduce. - Type-ability — the recursive return type
number | typeof nextis awkward in TypeScript; thevalueOfapproach is even harder to type. Mention it as a downside. - Alternatives — if the API is
sum(...nums), just use rest params. Currying is for partial application and lazy evaluation, not for show. - Debuggability — a chain of arrow-returning calls is hard to read in a stack trace; named inner functions help.
Common mistakes:
- Forgetting to capture the running total in a closure (using a module-level variable — fails for concurrent chains).
- Returning
next()(calling it) instead ofnext(the function reference) at the recursive step. - Using arrow class fields with state that should be per-instance (works but allocates).
- Not handling the empty-call termination case.
Interview-ready summary: "I'd return a function that's dual-purpose: with an argument it accumulates and returns itself, with no argument it returns the closed-over total. Each top-level call creates a fresh closure so chains are independent. The flashier valueOf variant skips the terminator but relies on implicit coercion, which I'd avoid in production."
Code
Follow-up questions
- •Implement a curry(fn, arity) that auto-curries any n-ary function.
- •Why doesn't sum(10)(20)(30) return 60 directly without a terminator?
- •What's the memory cost of a long curry chain?
Common mistakes
- •Forgetting the no-argument terminator and returning a function instead of the total.
- •Using `arguments` inside an arrow function — there is no `arguments`.
- •Not handling the first call's accumulation — initialising `total` outside `sum` (shared across calls).
Performance considerations
- •Each call creates a new closure frame. Fine for interview-scale; for hot paths, prefer accumulator patterns.
Edge cases
- •Calling `sum()` with no args first — guard with a default of 0.
- •Mixing the two approaches (terminator + valueOf) makes the API unpredictable. Pick one.
Real-world examples
- •Lodash's `_.curry` and Ramda's pervasive currying use the same closure-based technique.