Last active
December 12, 2021 18:08
-
-
Save toridoriv/e57e6f64d20525f2b8beeff01a2d8de8 to your computer and use it in GitHub Desktop.
Mostly strongly typed curry and uncurry functions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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