Implement curry(fn) — the most general member of the currying family. Where the classic curry forces one argument per call and the flexible variant still demands fn.length arguments before firing, this version makes no assumption about the original function's arity at all. The caller decides when the chain ends by signalling with an empty call. Every numeric (or otherwise non-empty) call accumulates more arguments and hands back another curried wrapper; the empty call () invokes the underlying function with everything collected so far.
This is the right shape when fn is variadic ((...args) => …), when its arity is dynamic, or when you simply don't want to commit to "exactly this many arguments" up front. It's the same terminator pattern as the sum question, but generalised: instead of a fixed adder, fn can be anything.
// curry takes any variadic function and returns a curried wrapper.
// Each call with at least one argument accumulates and returns
// another wrapper. A call with zero arguments fires fn with the
// accumulated arguments and returns its result.
function curry<T extends (...args: any[]) => any>(
fn: T
): (...args: any[]) => any;
// Variadic sum — the chain length is decided by the caller.
const add = curry((...args) => args.reduce((a, b) => a + b, 0));
add(1)(2, 3)(4)(); // 10
add(1, 2, 3, 4)(); // 10
add(); // 0 — fn called with no args
// Builder-style — collect parts, then commit.
const join = curry((...parts) => parts.join('/'));
join('users')('42')('posts')(); // "users/42/posts"
join('a', 'b')('c', 'd')(); // "a/b/c/d"
// Partial application + terminate. A partial is just a stored
// intermediate wrapper; calling it more accumulates further.
const addFive = curry((a, b) => a + b)(5);
addFive(3)(); // 8
addFive(10)(); // 15
// Single-argument fn — first call collects, second call fires.
const double = curry((x) => x * 2);
double(5)(); // 10
() with no arguments. Any call with at least one argument accumulates and returns another wrapper. Only the zero-argument call invokes the underlying function.typeof curry(fn)(1) is 'function'. The return type flips to "whatever fn returns" only on the empty call.const part = curry(fn)(1, 2); part(3)(); and part(30)(); must each compute their own result without polluting the other.fn.length is not consulted. Unlike curry and curry-ii, this implementation never looks at the function's arity. The signal to fire comes from the caller, not the function's declaration.fn can be any function. Variadic, fixed-arity, zero-arity — all are valid. A zero-arity fn fires immediately on curry(fn)().this on the terminator call. If a caller uses .call(ctx, ...) or .apply(ctx, ...) on the curried wrapper, that this should flow through to fn when it eventually runs.Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.