izuna/src/curry.js
2025-04-08 17:39:58 -05:00

83 lines
1.8 KiB
JavaScript

/**
* @template {(...args: any[]) => any} F
* @typedef {F extends ((...args: infer A) => any) ? A : never} Params
*/
/**
* @template {any[]} T
* @typedef {T extends [any, ...any[]] ? T[0] : never} Head
*/
/**
* @template {any[]} T
* @typedef {((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []} Tail
*/
/**
* @template {any[]} T
* @typedef {T extends ([] | [any]) ? false : true} HasTail
*/
/**
* @template {any[]} T
* @typedef {{ 0: Last<Tail<T>>; 1: Head<T> }[HasTail<T> extends true ? 0 : 1]} Last
*/
/**
* @template {any[]} T
* @typedef {T['length']} Length
*/
/**
* @template E
* @template {any[]} T
* @typedef {((head: E, ...args: T) => any) extends ((...args: infer U) => any) ? U : T} Prepend
*/
/**
* @template {number} N
* @template {any[]} T
* @template {any[]} [I = []]
* @typedef {{ 0: Drop<N, Tail<T>, Prepend<any, I>>; 1: T }[Length<I> extends N ? 1 : 0]} Drop
*/
/**
* @template X, Y
* @typedef {X extends Y ? X : Y} Cast
*/
/**
* @template {any[]} P, R
* @typedef {<T extends any[]>(...args: Cast<T, Partial<P>>) => Drop<Length<T>, P> extends [any, ...any[]] ? Curried<Cast<Drop<Length<T>, P>, any[]>, R> : R} Curried
*/
/**
* @template {any[]} P, R
* @param {number} arity
* @param {(...args: P) => R} func
* @returns {Curried<P, R>}
*/
export function curryN(arity, func) {
return function curried(...args) {
if (args.length >= arity) {
return func.apply(this, args)
} else {
/** @type {Curried<P, R>} */
return function(...args2) {
return curried.apply(this, args.concat(args2))
}
}
}
}
/**
* @template {any[]} P, R
* @param {(...args: P) => R} func
* @returns {Curried<P, R>}
*/
export function curry(func) {
return curryN(func.length, func)
}