00462-extreme-currying-2

Back

// Extract elements from array T excluding elements from array U.
type RestParemeters<T extends any[], U extends any[]> =
  T extends [...U, ...infer R] ? R : never

type Currying<
  T extends (...args: any[]) => any,
  P extends any[] = Parameters<T>,
> = P['length'] extends 0
  ? ReturnType<T>
  : <S extends any[]>(...args: S) => Currying<(...args: RestParemeters<P, S>) => ReturnType<T>>

declare function DynamicParamsCurrying<T extends (...args: any[]) => any>(fn: T): Currying<T>

Solution by Heonys #32482

type Curried<P extends unknown[], R> = P["length"] extends 0
  ? R
  : <S extends any[]>(
      ...arg: S
    ) => P extends [...S, ...infer T] ? Curried<T, R> : R;

declare function DynamicParamsCurrying<P extends unknown[], R>(fn: (...args: P) => R): Curried<P, R>

Solution by vangie #32351

This solution is longer but slightly better than the canonical one (https://github.com/type-challenges/type-challenges/issues/3697) in that it takes care to preserve the names of the function parameters.

Thanks #3697 and #28634 !


type FirstAsTuple<T extends any[]> = T extends [any, ...infer R]
  ? T extends [...infer F, ...R]
    ? F
    : never
  : never;

type Curry<A, R, D extends unknown[] = []> =
    A extends [unknown, ...infer T]
      ? T extends []
        ? (...args: [...D, ...FirstAsTuple<A>]) => R
        : ((...args: [...D, ...FirstAsTuple<A>]) => Curry<T, R>) & Curry<T, R, [...D, ...FirstAsTuple<A>]>
      : () => R

declare function DynamicParamsCurrying<A extends unknown[], R>(fn: (...args: A) => R): Curry<A, R>

Compare: image vs: image

Solution by wzc520pyfm #31816

type Remove<T extends any[],U extends any[]> = 
[T,U] extends [[infer TF,...infer TR],[infer UF,...infer UR] ]?
  Remove<TR,UR>
  :T

type Overloads<T extends any[]> = 
T extends [infer F,...infer L]?
  [F] | [F,...Overloads<L>] | []
  : []

type Curried<P extends any[],R extends any> = 
P extends [infer F,...infer L]?
  <K extends Overloads<L>>(arg:F,...rest:K)=> Curried<Remove<L,K>,R >
  :R


declare function DynamicParamsCurrying<P extends any[],R>(fn: (...args:P)=>R ): Curried<P,R>

Solution by jiangshanmeta #30500

// your answers


type GetFuncQuery<T> = T extends (...x: infer QueryArray) => infer Res ? QueryArray : any

type GetFuncRes<T> = T extends (...x: infer QueryArray) => infer Res ? Res : any

type GetRestQuery<A extends any[], B extends any[]> = A extends [...B, ...infer Rest] ? Rest : []

type GetCurFunc<QueryArray extends any[], Res> = <F extends any[]>(...x: F) => QueryArray['length'] extends F["length"] ? Res : GetCurFunc<GetRestQuery<QueryArray, F>, Res>

declare function DynamicParamsCurrying<T>(fn: T): GetCurFunc<GetFuncQuery<T>, GetFuncRes<T>>

Solution by 437204933 #29935

type PartialTuple<T extends any[], U extends T[number][] = []> = T extends [infer F, ...infer R] ? [...U, F] | PartialTuple<R, [...U, F]> : []

type Tails<T extends any[]> = T extends [any, ...infer R] ? R : never

type Rest<T extends any[], U extends any[], R extends any[] = []> = R['length'] extends U['length'] ? T : Rest<Tails<T>, U, [...R, unknown]>

type Curry<T extends any[], R, Args extends any[] = PartialTuple<T>> = <TargetArgs extends Args>(...args: TargetArgs) => (TargetArgs['length'] extends T['length'] ? R : Curry<Rest<T, TargetArgs>, R>)

declare function DynamicParamsCurrying<T extends any[], R>(fn: (...args: T) => R): Curry<T, R>

Solution by taiyuuki #29916

// 你的答案
type Curried<CurArgs extends any[], L, R> = <Args extends any[]>(...args: Args) => [...CurArgs, ...Args]['length'] extends L ? R : Curried<[...CurArgs, ...Args], L, R>
declare function DynamicParamsCurrying<Args extends any[], R>(fn: (...args: Args) => R): Curried<[], Args['length'], R>

Solution by AAA611 #28859

// this solution covers a few more edage cases such as dealing with optional parameters
// taking a function that has no parameters to start with
// at least one argument needs to be passed on each call
// you must pass the correct parameters

// Optional<> makes all the params optional except for the first one
// turning exactOptionalPropertyTypes on will prevent passing undefined unless
// the original function prop is optional or has undefined explicitly in it's union
// you must pass undefined even if exactOptionalPropertyTypes is true in your config and
// undefined isn't in the optional params union
// so that the curried function wont assume you've finished passing args when
// you've just not got to your optional ones yet
type Optional<T extends any[]> = {[K in keyof T]?: T[K]}
  & [T[0], ...any[]]

// Split<> moves array items from right array to left array, recursively one by one
// until the length of the left array matches the specified number
// Currying uses this to grab the remaining params from the right array
type Split<N extends number, Right,  Left extends any[] = []> =
    Left['length'] extends N
      ? [Left, Right]
      : Right extends [infer First, ...infer Rest]
        ? Split<N, [...Rest], [...Left, First]>
        : never

// using Required<Args> to get the max number of params incase of optional ones
type Currying<Fn> = Fn extends (...args:infer Args) => infer R
  ? Required<Args>['length'] extends 0 // if Args['length'] is 0, the function passed has no args
    ? Fn // so we return the function
    : <A2 extends Optional<Args>>(...args2:A2) => // We make the params optional so any number can be passed
      A2 extends [...infer Args2] // then we infer what was actaully passed
        ? Args2['length'] extends Required<Args>['length'] // if passed args length is the same as Args length
          ? R // then all args have been passed and we return the return value
          : Currying<(...args3: Split<Args2['length'], Args>[1]) => R> // else we get the remaing params and recursively pass them to Currying
        : never
  : never

declare function DynamicParamsCurrying<Fn extends (...args: any[]) => any>(fn:Fn): Currying<Fn>

Solution by fallenfreq #26918

type Currying<T extends any[], U> = T extends [] ? U : <_Args extends any[]>(...args: _Args/*反传*/) => T extends [..._Args, ...infer R] ? Currying<R, U> : never;

declare function DynamicParamsCurrying<T extends any[], U>(fn: (...args: T) => U): Currying<T, U>;

Solution by E-uler #25580

type Fn = (...args: any[]) => any

type Func<Args extends any[], Ret> = (...args: Args) => Ret

type PopArgs<Args extends any[]> = Args extends [any?]
  ? []
  : Args extends [...infer Items, any?]
  ? Items
  : never

type RestArgs<Args extends any[], Items extends any[]> = Args extends [...Items, ...infer Rest]
  ? Rest
  : never

type Currying<Args extends any[], Ret, A extends any[] = Args, R = Ret> = number extends A['length']
  ? never
  : A extends [any?]
  ? Func<A, R>
  : Currying<Args, Ret, PopArgs<A>, Currying<RestArgs<Args, PopArgs<A>>, Ret>> & Func<A, R>

type Curry<F extends Fn> = Currying<Parameters<F>, ReturnType<F>>

declare function DynamicParamsCurrying<F extends Fn>(fn: F): Curry<F>

Solution by 567563383 #24303

declare function DynamicParamsCurrying<A extends unknown[], R> (fn: (...args: A) => R): Curry<A, R>

type Curry<A extends unknown[], R> =
  <Args extends unknown[]>(...args: Args) => A extends [...Args, ...infer Rest]
    ? Rest['length'] extends 0
      ? R
      : Curry<Rest, R>
    : never

Solution by drylint #23325

type CurriedResult<FA extends unknown[], FR> = 
  FA extends []
  ? FR
  : <FRFA extends unknown[]>(...args: FRFA) =>
      FA extends [...FRFA, ...infer FARest]
      ? CurriedResult<FARest, FR>
      : never

declare function DynamicParamsCurrying<FA extends unknown[], FR>(fn: (...args: FA) => FR): CurriedResult<FA, FR>

playground

Solution by zhaoyao91 #22172

// 你的答案 // 看了别人的代码后自己复习了一边.实在太漂亮了. // 原作者 mfish33 // 原代码 https://github.com/type-challenges/type-challenges/issues/20779

type CurriedFn<A extends any[], R, T extends any[] = []>
    = A extends [infer F, ... infer L]
    ? ((..._: [...T, F]) => (L extends [] ? R : CurriedFn<L, R>))
    & CurriedFn<L, R, [...T, F]>
    : () => R

Solution by goddnsgit #22136

I think this is shorter and more concise than other solutions. Effectively it just generates all possible function definitions of the curried function and does an intersection.

declare function DynamicParamsCurrying<TArgs extends any[], TRet>(fn: (...args:TArgs) => TRet): CreateChains<TArgs, TRet>

type CreateChains<TArgs extends readonly any[], TRet, Bucket extends any[] = []> = 
  TArgs extends [infer First, ... infer Tail] ?
      CreateChains<Tail, TRet, [...Bucket, First]> &
      (Bucket["length"] extends 0 ?
        (...args: never) => any :
        ((...args: Bucket) => CreateChains<Tail, TRet, [First]>)) :
      Bucket["length"] extends 0 ?
        TRet :
      (...args: Bucket) => TRet

Solution by mfish33 #20779

type UnionToIntersection<U> = [
  U extends unknown ? (arg: U) => any : never
] extends [(arg: infer T) => any]
  ? T
  : never

type SplitParameters<T extends unknown[], U extends unknown[] = []> =
  | [U, T]
  | (T extends [infer A, ...infer B] ? SplitParameters<B, [...U, A]> : never)

type CurriedFn<
  P extends unknown[],
  R,
  SP extends [unknown[], unknown[]] = SplitParameters<P>
> = P['length'] extends 0
  ? R
  : UnionToIntersection<
      SP extends unknown ? (...args: SP[0]) => CurriedFn<SP[1], R> : never
    >

declare function DynamicParamsCurrying<P extends unknown[], R>(
  fn: (...args: P) => R
): CurriedFn<P, R>

Solution by theoolee #19858

declare function DynamicParamsCurrying<T extends any[], R>(fn: (...args: T) => R):  
  T extends [] ? R : // returns R if no params is needed
    <P extends any[]>(...args: P) => 
      T extends [...P, ...infer K3] ? // check does P & K3 extends T, basically checking is P fully T
        ReturnType<typeof DynamicParamsCurrying<K3, R>> : // Pass in K3 and T to check is K3 = T
      R;

Solution by Mantou1233 #18047

// your answers
declare function DynamicParamsCurrying<
    T extends readonly any[],
    S extends any
    >(fn:(...args:T)=>S): 
    T extends []
    ? S
    : <R extends readonly any[]>(...args:R)=>
    T extends [...R,...infer U] 
    ? ReturnType<typeof DynamicParamsCurrying<U,S>> 
    : never ;

Solution by justBadProgrammer #16231

type CurryingType<T extends unknown[], R> =
  <P extends Partial<T>>(...args: P) => ((...args: T) => any) extends ((...args: [...P, ...infer Args]) => any)
    ? Args extends []
      ? R
      : CurryingType<Args, R>
    : never

declare function DynamicParamsCurrying<T extends (...args: any[]) => any>(fn: T): CurryingType<Parameters<T>, ReturnType<T>>

Solution by XkSuperCool #15988

function curried<
  T extends (...args: any) => any
>(func: T): DynamicCurrying<T> {
  if(!hasParms(func)) return func;
  return (
    (...args: any) => args.length === func.length
      ? func(...args)
      : curried(func.bind(null, ...args))
  ) as any;
}

type DynamicCurrying<
  T extends (...args: any) => any,
  Args extends any[] = Parameters<T>,
> = Args extends [] ? T :
  <SubArgs extends OptionalArgs<Args>>(...arg: SubArgs) =>
    Args['length'] extends SubArgs['length']
      ? ReturnType<T>
      : DynamicCurrying<(...args: Remove<Args, SubArgs>) => ReturnType<T>>;

type OptionalArgs<T extends any[]> =
  T extends [...infer Rest, infer Last]
    ? T | OptionalArgs<Rest> : never;

type Remove<
  From extends any[],
  Length extends any[],
> = Length extends [...infer Rest, infer Last]
  ? Remove<Shift<From>, Rest> : From;

type Shift<T extends any[]> =
  T extends [infer First, ...infer Rest]
    ? Rest : [];

TypeScript Playground

Solution by willgm #15749

type Currying<A extends any[], R> = <D extends any[]>(...args: D) => A extends [...D, ...infer O] ? O extends [] ? R : Currying<O, R> : never;
declare function DynamicParamsCurrying<T>(fn: T): T extends (...args: infer A) => infer R ? Currying<A, R extends true ? boolean : never> : never;

Solution by limerickgds #13943

type PopFromArray<T extends any[], TNumber extends number> = T extends [...infer TFirst, infer TSecond] ? TFirst['length'] extends TNumber ? [TSecond] : [...PopFromArray<TFirst, TNumber>,TSecond] : [];
type BuildParameters<T extends any[]> = <TParams extends any[]>(...params: TParams) => TParams['length'] extends T['length'] ? boolean : BuildParameters<PopFromArray<T, TParams['length']>>;

declare function DynamicParamsCurrying<TFunction extends (...params: any[]) => boolean>(fn: TFunction): BuildParameters<Parameters<TFunction>>;

Solution by HiiiiD #13669

declare function DynamicParamsCurrying<Fn extends (...args: any) => any>(
  fn: Fn,
): CurriedType<ReturnType<Fn>, Parameters<Fn>>;

type Func<Params, Ret> = (
  ...params: Params extends unknown[] ? Params : never
) => Ret;

type CurriedType<Ret, Params, Current = []> = Params extends [
  ...infer Rest,
  infer Last,
]
  ? Rest extends []
    ? Func<Params, Current extends [] ? Ret : CurriedType<Ret, Current>>
    : Func<Params, Current extends [] ? Ret : CurriedType<Ret, Current>> &
        CurriedType<
          Ret,
          Rest,
          [Last, ...(Current extends unknown[] ? Current : never)]
        >
  : never;

Solution by ryo-mf-fjt #11811

type UnionToIntersection<U> = (U extends any ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I : never;

type HeadArg<T extends (...args: any) => any> = Parameters<T> extends [any, ...infer Rest]
  ? Parameters<T> extends [...infer First, ...Rest] ? First : never
  : never;

type TailArgs<T extends (...args: any) => any> = Parameters<T> extends [infer First, ...any[]]
  ? Parameters<T> extends [First, ...infer Rest] ? Rest : never
  : never;

type PrefixArgs<U extends any[], T extends (...args: any) => any> = T extends any
  ? (...args: [...U, ...Parameters<T>]) => ReturnType<T>
  : never;

type Curry<T extends (...args: any) => any> = T extends (...args: [any, ...any[]]) => any
  ? Parameters<T>['length'] extends 0 | 1
    ? T
    : ((...args: [...HeadArg<T>]) => UnionToIntersection<Curry<(...args: [...TailArgs<T>]) => ReturnType<T>>>)
      | PrefixArgs<HeadArg<T>, Curry<(...args: [...TailArgs<T>]) => ReturnType<T>>>
  : never;

declare function DynamicParamsCurrying<T extends (...args: any[]) => any>(func: T): UnionToIntersection<Curry<T>>;

const test = DynamicParamsCurrying((a: 1, b: 2, c: 3, d: 4) => 'result');
// typeof test:
// ((a: 1) =>
//   ((b: 2) => ((c: 3) => (d: 4) => string) & ((c: 3, d: 4) => string))
//   & ((b: 2, c: 3) => (d: 4) => string)
//   & ((b: 2, c: 3, d: 4) => string))
// & ((a: 1, b: 2) =>
//   ((c: 3) => (d: 4) => string)
//   & ((c: 3, d: 4) => string))
// & ((a: 1, b: 2, c: 3) => (d: 4) => string)
// & ((a: 1, b: 2, c: 3, d: 4) => string)

Solution by MikeJerred #11759

type Currying<Props extends unknown[], Return, Args extends unknown[] = []> =
  Props extends [infer First] ? (
    (...args: [...Args, First]) => Return
  ) : Props extends [infer First, ...infer Rest] ? (
    ((...args: [...Args, First]) => Currying<Rest, Return>) & Currying<Rest, Return, [...Args, First]>
  ) : () => Return

declare function DynamicParamsCurrying<Props extends unknown[], Return>(fn: (...args: Props) => Return): Currying<Props, Return>

Playground

Solution by teamchong #11383

type CurryFn<A extends unknown[], R, O extends unknown[] = []> = <ArgIn extends unknown[]>(...arg: ArgIn) => [...ArgIn, ...O]['length'] extends A['length'] ? R : CurryFn<A, R, [...ArgIn, ...O]>

declare function DynamicParamsCurrying<Fn>(fn: Fn): Fn extends (...arg: infer Arg) => infer Return ? 
CurryFn<Arg,ReturnType<Fn>>
: never

Solution by jackluson #9545

declare function DynamicParamsCurrying<Fn extends (...arg: any) => any>(fn: Fn): Fn extends (...arg: infer P) => ReturnType<Fn> ? Currying<P,ReturnType<Fn>> : never

type Currying<P,Ret> = P extends [infer X,...infer R] ? <T extends any[]>(...arg: T) => Currying<P extends [...T,...infer Rest] ? Rest : never ,Ret> : Ret

Solution by KK-AI-LL #8272

// 你的答案
type CurriedDynamicFunction<T extends unknown[], R, H extends unknown[] = []> = T extends [
  infer L,
  ...infer N
]
  ? {
      (...args: [...H, L]): CurriedFunction<N, R>
    } & (N extends [] ? {} : CurriedDynamicFunction<N, R, [...H, L]>)
  : R

type CurriedFunction<T extends unknown[], R> = T extends []
  ? R
  : { (): CurriedFunction<T, R> } & CurriedDynamicFunction<T, R>

declare function DynamicParamsCurrying<T extends unknown[], R>(
  func: (...args: T) => R,
): T extends [] ? { (): R } : CurriedFunction<T, R>

Solution by ZangYuSong #6835

type Drop<N, T extends any[], A extends any[] = []>
     = A['length'] extends N ? T 
     : T extends [infer L, ...infer R] ? Drop<N, R, [[], ...A]> : []

type PrefixUnion<T> = T extends [...infer L, infer R] ? T | PrefixUnion<L> : never

type DPC<Args extends any[], Ret>
    = Args['length'] extends 0 ? Ret extends true ? boolean : Ret
    : <T extends PrefixUnion<Args>>(...x: T) => DPC<Drop<T['length'], Args>, Ret> 

declare function DynamicParamsCurrying<F>(fn: F): F extends (...x: infer A) => infer R ? DPC<A, R> : never

Solution by okayu29 #6311

// your answers
type Curried<A, R, Args extends any[] = []> = 
  A extends [] ? R :
  A extends [infer Head, ...infer Tail] 
    ? { (...args: [...Args, Head]): Curried<Tail, R> } & (Tail extends [] ? {} : Curried<Tail, R, [...Args, Head]>)
    : never

declare function DynamicParamsCurrying<Args extends any[], Ret>(fn: (...args: Args) => Ret): 
  Curried<Args, Ret>

Solution by Sobes76rus #5813

/**
 * Partialize the tuple to union of part of the tuple.
 * 
 * e.g. `[string, number, boolean]` -> `[string] | [string, number] | [string, number, boolean]`
 */
type PartialTuple<T extends unknown[]> = T extends [infer Head, ...infer Tail]
  ? [Head] | [Head, ...PartialTuple<Tail>]
  : []

/**
 * Returns a tuple removed first elements `U` from `T`.
 */
type DiffOfTuple<T extends unknown[], U extends unknown[]> = T extends [...U, ...infer Tail]
  ? Tail
  : never

/**
 * Normalize tuple type.
 * 
 * This type will weaken `T` to `U`, e.g. `[123]` to `[number]`.
 */
type Normalize<T extends unknown[], U extends unknown[]> = U extends U ? T extends U ? U : never : never

/**
 * Cast `T` as `U`.
 */
type Cast<T, U> = T extends U ? T : never

/**
 * Dynamic Curried Function.
 */
type DynamicCurried<T extends unknown[], R> = <U extends PartialTuple<T>> (...args: U) => DynamicCurriedResult<
  DiffOfTuple<T, Normalize<U, PartialTuple<T>>>,
  R
>

/**
 * Dynamic Curried Function Return Value.
 */
type DynamicCurriedResult<T extends unknown[], R> = T extends [] ? R : DynamicCurried<T, R>

/**
 * 462 - Currying 2
 */
declare function DynamicParamsCurrying<T extends unknown[], R> (fn: (...args: T) => R): DynamicCurried<T, R>

Solution by shogogg #5445