Skip to content
VersionSize

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); // 6

Partial 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 on fn.length.
  • Throws TypeError if the argument is not a function.

See Also

  • compose: Functional composition from right to left.
  • pipe: Functional composition from left to right.
  • fp: Wrap functions for functional programming styles.