curry
The curry utility transforms a function that takes multiple arguments into a sequence of functions, each taking a single argument. It is a fundamental tool for functional programming, enabling easy partial application and better logic reuse.
Implementation
View Source Code
ts
/** biome-ignore-all lint/suspicious/noExplicitAny: - */
/**
* Curries a function, allowing it to be called with partial arguments.
*
* @example
* ```ts
* const add = (a: number, b: number) => a + b;
* const curriedAdd = curry(add);
* curriedAdd(1)(2) // 3;
* ```
*
* @param fn - The function to curry.
* @param arity - The number of arguments the function expects. Defaults to the function's length.
*
* @returns A curried version of the function.
*/
// Take first N items from tuple T
type Take<N extends number, T extends readonly unknown[], Acc extends readonly unknown[] = []> = Acc['length'] extends N
? Acc
: T extends readonly [infer H, ...infer R]
? Take<N, R, readonly [...Acc, H]>
: Acc;
// Curried: at each step, accept a non-empty tuple of args A,
// ensure it's a prefix of P, then either return R (if done) or recurse.
export type Curried<P extends readonly unknown[], R> = P extends readonly []
? () => R
: <A extends readonly [unknown, ...(readonly unknown[])]>(
...args: A
) => P extends readonly [...A, ...infer Rest] ? (Rest extends readonly [] ? R : Curried<Rest, R>) : never;
// Overloads to reflect default arity vs. a custom arity.
export function curry<T extends (...args: any[]) => any>(fn: T): Curried<Parameters<T>, ReturnType<T>>;
export function curry<T extends (...args: any[]) => any, N extends number>(
fn: T,
arity: N,
): Curried<Take<N, Parameters<T>>, ReturnType<T>>;
// Runtime implementation
export function curry(fn: (...args: any[]) => any, arity = fn.length) {
const curried = (...args: any[]): any => {
if (args.length >= arity) {
return fn(...args);
}
return (...rest: any[]) => curried(...args, ...rest);
};
return curried as any;
}Features
- Isomorphic: Works in both Browser and Node.js.
- Dynamic Arity: Automatically determines how many arguments to wait for based on the original function's length.
- Type-safe: Properly infers the return types and argument types through the currying chain.
API
ts
function curry<T extends (...args: any[]) => any>(fn: T): (...args: any[]) => any;Parameters
fn: The function to curry.
Returns
- A curried version of the input function.
Examples
Basic Currying
ts
import { curry } from '@vielzeug/toolkit';
const add = (a: number, b: number, c: number) => a + b + c;
const curriedAdd = curry(add);
curriedAdd(1)(2)(3); // 6Partial Application
ts
import { curry, map } from '@vielzeug/toolkit';
const multiply = curry((a: number, b: number) => a * b);
// Create a reusable 'double' function
const double = multiply(2);
double(10); // 20
map([1, 2, 3], double); // [2, 4, 6]Implementation Notes
- Performance-optimized using a recursive wrapper.
- Does not support functions with variadic arguments (
...args) effectively, as it relies onfn.length. - Throws
TypeErrorif the argument is not a function.