Skip to content

Instantly share code, notes, and snippets.

@toridoriv
Last active December 12, 2021 18:08
Show Gist options
  • Save toridoriv/e57e6f64d20525f2b8beeff01a2d8de8 to your computer and use it in GitHub Desktop.
Save toridoriv/e57e6f64d20525f2b8beeff01a2d8de8 to your computer and use it in GitHub Desktop.
Mostly strongly typed curry and uncurry functions.
/**
* @module arity
* Made using a Deno v1.16.14 and Typescript v4.4.2
*/
/**
* Describes literally any array, including empty arrays or arrays with items
*/
export type AnyArray = any[];
/**
* Describes any possible function, with zero or more parameters and any return type
*/
export type AnyFunction<Parameters extends AnyArray = any[]> = (
...params: Parameters
) => any;
/**
* Describes the type of the first item in an array, if it exists
*/
export type FirstItem<List extends AnyArray> = List extends Array<unknown>
? List[0]
: never;
/**
* Describes the type of parameters a function has
*/
export type Params<Callable> = Callable extends (...params: infer P) => any ? P
: never;
/**
* Describes the type of parameters a function has after removing the first parameter
*/
export type RestParameters<Parameters extends AnyArray> =
AnyFunction<Parameters> extends (_: any, ...params: infer Rest) => any ? Rest
: never;
/**
* Describes the literal length of an array
*/
export type Length<List extends AnyArray> = List["length"];
/**
* Describes if an array has items or not
*/
export type HasLength<List extends AnyArray, N extends number> =
Length<List> extends N ? true : false;
/**
* Describes a function that has been curried
*/
export type Curried<Fn extends AnyFunction> = (
parameter: FirstItem<Params<Fn>>,
) => HasLength<Params<Fn>, 1> extends true ? ReturnType<Fn>
: Curried<(...params: RestParameters<Params<Fn>>) => ReturnType<Fn>>;
export type Uncurried<Fn extends AnyFunction> = (
...params: AnyArray
) => ReturnType<Fn>;
/**
* Curries a function
* @param {function} fn
* @returns {Curried}
* @example
* ```ts
* import { curry } from "./arity.ts"
*
* const sum = (a: number, b: number) => a + b;
* const curriedSum = curry(sum);
* const addOneTo = curriedSum(1);
*
* console.log(addOneTo(2)); // 3
* console.log(addOneTo(3)); // 4
* ```
*/
export function curry<Fn extends AnyFunction>(fn: Fn): Curried<Fn> {
const arity = fn.length;
return function $curry(...args): ReturnType<Curried<Fn>> {
return arity > args.length
? $curry.bind(null, ...args)
: fn.call(null, ...args);
};
}
/**
* Uncurries a curried function
* @param {function} fn
* @returns {Uncurried}
* @example
* ```ts
* import { uncurry } from "./arity.ts"
*
* const sum = (a: number) => (b: number) => a + b;
* const uncurriedSum = uncurry(sum);
*
* console.log(uncurriedSum(1, 2)); // 3
* console.log(uncurriedSum(2, 2)); // 4
* ```
*/
export function uncurry<Fn extends AnyFunction>(fn: Fn): Uncurried<Fn> {
return (...args) => args.reduce((fn, arg) => fn(arg), fn);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment