// 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: vs:
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>
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 : [];
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>
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