npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tomsons/fp-utils

v0.5.0

Published

A TypeScript functional programming utilities library that provides type-safe currying and other functional programming primitives later.

Readme

fp-utils

A TypeScript functional programming utilities library that provides type-safe currying and other functional programming primitives later.

Installation

npm install @tomsons/fp-utils
# or
pnpm add @tomsons/fp-utils
# or
yarn add @tomsons/fp-utils

Features

  • Type-Safe Currying: Transform multi-parameter functions into curried functions with full TypeScript type inference
  • Partial Application: Support for both single-parameter currying and multi-parameter partial application
  • Zero Dependencies: Lightweight library with no external dependencies
  • Full TypeScript Support: Complete type safety throughout the currying chain

API Reference

curryfy<T>(fn: T): CurriedFunction<T>

Transforms a function with multiple parameters into a curried function that can be called with one argument at a time, maintaining full type safety throughout the currying chain.

Parameters

  • fn - The function to curry. Must be a function with a fixed number of parameters.

Returns

A curried version of the input function that can be called one parameter at a time.

Usage Examples

Basic Currying

import { curryfy } from '@tomsons/fp-utils';

// Simple addition function
const add = (a: number, b: number, c: number) => a + b + c;
const curriedAdd = curryfy(add);

// Call with one argument at a time
const result = curriedAdd(1)(2)(3); // 6

// Or store intermediate functions
const addOne = curriedAdd(1);
const addOneAndTwo = addOne(2);
const finalResult = addOneAndTwo(3); // 6

Practical Example: Calculator

import { curryfy } from '@tomsons/fp-utils';

const calculator = (operation: string, a: number, b: number): number => {
  switch (operation) {
    case 'add': return a + b;
    case 'multiply': return a * b;
    case 'subtract': return a - b;
    case 'divide': return a / b;
    default: return 0;
  }
};

const curriedCalc = curryfy(calculator);

// Create specialized functions
const adder = curriedCalc('add');
const multiplier = curriedCalc('multiply');

console.log(adder(5)(3)); // 8
console.log(multiplier(4)(3)); // 12

// Reuse the same partially applied function
console.log(adder(10)(2)); // 12
console.log(adder(7)(8)); // 15

String Formatting

import { curryfy } from '@tomsons/fp-utils';

const formatter = (prefix: string, value: any, suffix: string) => 
  `${prefix}${value}${suffix}`;

const curriedFormatter = curryfy(formatter);

// Create specialized formatters
const bracketFormatter = curriedFormatter('[');
const braceFormatter = curriedFormatter('{');

console.log(bracketFormatter(42)(']')); // "[42]"
console.log(braceFormatter('hello')('}')); // "{hello}"

// Create even more specialized functions
const arrayFormatter = bracketFormatter;
const objFormatter = braceFormatter;

console.log(arrayFormatter('item1')(']')); // "[item1]"
console.log(objFormatter('key: value')('}')); // "{key: value}"

Working with Mixed Types

import { curryfy } from '@tomsons/fp-utils';

interface User {
  id: number;
  name: string;
  active: boolean;
}

const createUserMessage = (greeting: string, user: User, punctuation: string) =>
  `${greeting}, ${user.name}${punctuation} Your ID is ${user.id} and you are ${user.active ? 'active' : 'inactive'}.`;

const curriedMessage = curryfy(createUserMessage);

// Create greeting templates
const welcomeMessage = curriedMessage('Welcome');
const helloMessage = curriedMessage('Hello');

const user: User = { id: 123, name: 'Alice', active: true };

console.log(welcomeMessage(user)('!')); 
// "Welcome, Alice! Your ID is 123 and you are active."

console.log(helloMessage(user)('.'));
// "Hello, Alice. Your ID is 123 and you are active."

Complex Data Processing

import { curryfy } from '@tomsons/fp-utils';

const processData = (
  transformer: (x: number) => number,
  filter: (x: number) => boolean,
  data: number[]
) => data.filter(filter).map(transformer);

const curriedProcess = curryfy(processData);

// Create specialized processors
const doublePositives = curriedProcess(x => x * 2)(x => x > 0);
const squareEvens = curriedProcess(x => x ** 2)(x => x % 2 === 0);

console.log(doublePositives([1, -2, 3, -4, 5])); // [2, 6, 10]
console.log(squareEvens([1, 2, 3, 4, 5, 6])); // [4, 16, 36]

Runtime Partial Application

While TypeScript enforces single-parameter currying for type safety, the runtime behavior supports partial application with multiple arguments:

import { curryfy } from '@tomsons/fp-utils';

const add4 = (a: number, b: number, c: number, d: number) => a + b + c + d;
const curriedAdd4 = curryfy(add4) as any; // Type assertion for runtime behavior

// All of these produce the same result:
console.log(curriedAdd4(1)(2)(3)(4));     // 10 - single args
console.log(curriedAdd4(1, 2)(3, 4));     // 10 - partial application
console.log(curriedAdd4(1, 2, 3)(4));     // 10 - partial application
console.log(curriedAdd4(1, 2, 3, 4));     // 10 - all args at once

Advanced Usage

Function Composition with Currying

import { curryfy } from '@tomsons/fp-utils';

const pipe = <T>(...fns: Array<(arg: T) => T>) => (value: T) =>
  fns.reduce((acc, fn) => fn(acc), value);

const multiply = (factor: number, value: number) => value * factor;
const add = (addend: number, value: number) => value + addend;

const curriedMultiply = curryfy(multiply);
const curriedAdd = curryfy(add);

// Create a processing pipeline
const processNumber = pipe(
  curriedMultiply(2),    // double the number
  curriedAdd(10),        // add 10
  curriedMultiply(0.5)   // halve the result
);

console.log(processNumber(5)); // ((5 * 2) + 10) * 0.5 = 10

Creating Configuration-based Functions

import { curryfy } from '@tomsons/fp-utils';

interface Config {
  prefix: string;
  suffix: string;
  transform: (s: string) => string;
}

const configurableFormatter = (config: Config, input: string) =>
  `${config.prefix}${config.transform(input)}${config.suffix}`;

const curriedFormatter = curryfy(configurableFormatter);

// Create different formatters with different configurations
const upperFormatter = curriedFormatter({
  prefix: '>>> ',
  suffix: ' <<<',
  transform: s => s.toUpperCase()
});

const lowerFormatter = curriedFormatter({
  prefix: '[ ',
  suffix: ' ]',
  transform: s => s.toLowerCase()
});

console.log(upperFormatter('Hello World')); // ">>> HELLO WORLD <<<"
console.log(lowerFormatter('Hello World')); // "[ hello world ]"

Type Safety

The curryfy function provides complete type safety throughout the currying chain:

import { curryfy } from '@tomsons/fp-utils';

const typedFunction = (str: string, num: number, bool: boolean) => ({
  string: str.toUpperCase(),
  number: num * 2,
  boolean: !bool
});

const curried = curryfy(typedFunction);

// TypeScript knows the exact type at each step:
const step1 = curried('hello');        // (num: number) => (bool: boolean) => Result
const step2 = step1(42);               // (bool: boolean) => Result
const result = step2(true);            // Result

// TypeScript will catch type errors:
// const error = curried(123);         // ❌ Error: Expected string, got number
// const error2 = step1('invalid');    // ❌ Error: Expected number, got string

Important Notes

Function Length and Default Parameters

The curryfy function uses fn.length to determine when all required parameters have been provided. This means:

// This function has length 1 (only counts required parameters)
const withDefaults = (a: number, b: number = 10, c: number = 20) => a + b + c;
const curriedDefaults = curryfy(withDefaults);

console.log(curriedDefaults(5)); // 35 (5 + 10 + 20)
// Only curries the first parameter since others have defaults

Rest Parameters

Functions with rest parameters are not suitable for currying since they don't have a fixed arity:

// This works but only curries the fixed parameters
const sumWithRest = (a: number, b: number, ...rest: number[]) => 
  a + b + rest.reduce((sum, val) => sum + val, 0);

const curriedSum = curryfy(sumWithRest);
console.log(curriedSum(1)(2)); // 3 (rest parameters ignored in currying)

Building

Run nx build fp-utils to build the library.

Running unit tests

Run nx test fp-utils to execute the unit tests via Vitest.

License

MIT License - see LICENSE file for details.