/** * @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>; 1: Head }[HasTail 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, Prepend>; 1: T }[Length extends N ? 1 : 0]} Drop */ /** * @template X, Y * @typedef {X extends Y ? X : Y} Cast */ /** * @template {any[]} P, R * @typedef {(...args: Cast>) => Drop, P> extends [any, ...any[]] ? Curried, P>, any[]>, R> : R} Curried */ /** * @template {any[]} P, R * @param {number} arity * @param {(...args: P) => R} func * @returns {Curried} */ export function curryN(arity, func) { return function curried(...args) { if (args.length >= arity) { return func.apply(this, args) } else { /** @type {Curried} */ return function(...args2) { return curried.apply(this, args.concat(args2)) } } } } /** * @template {any[]} P, R * @param {(...args: P) => R} func * @returns {Curried} */ export function curry(func) { return curryN(func.length, func) }