00017-hard-currying-1

Back

declare function Currying<F>(fn: F): Curried<F>

type Curried<F> = F extends (...args: infer A) => infer R
  ? A extends [infer First, ...infer Rest]
  ? (arg: First) => Rest['length'] extends 0 ? R : Curried<(...args: Rest) => R>
  : () => R
  : never

Solution by wendao-liu #35165

type Curried<T> = T extends  (...args: infer A) => infer R ? A extends [] ? () => R : A extends [infer P] ? (p: P) => R : (A extends [infer V, ...infer rest] ? (p: V) => Curried<(...args: rest) => R> : never) : never
declare function Currying<T>(fn: T): Curried<T>

Solution by ouzexi #34196

type Curried<F> = F extends (...args: infer Args) => infer Ret
    ? Args['length'] extends 0 | 1
        ? F
        : Args extends [any, ...infer RestArgs]
            ? (...args: Args extends [infer Arg, ...any] ? [Arg] : never) => Curried<(...args: RestArgs) => Ret>
            : never
    : never

declare function Currying<T extends Function>(
    fn: T
): Curried<T>

Solution by Mumujianguang #34000

type Curried<T> = T extends (...args: infer P) => infer R
  ? P["length"] extends 0 | 1
    ? T
    : P extends [infer F, ...infer L]
    ? (arg: F) => Curried<(...args: L) => R>
    : never
  : never;

declare function Currying<T>(
  fn: T
): Curried<T>;

Solution by vangie #32231

// your answers
type Curried<Fn> = 
  Fn extends (...args: infer A) => infer R ?
  A extends [infer First, ...infer Rest] ?
  (arg: First) => Curried<(...args: Rest) => R> :
  R :
  never

declare function Currying<Fn>(fn: Fn):
  Fn extends (...args: infer A) => infer R ?
  A['length'] extends 0 ?
  () => R :
  Curried<Fn> :
  never

/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

const curried1 = Currying((a: string, b: number, c: boolean) => true)
const curried2 = Currying((a: string, b: number, c: boolean, d: boolean, e: boolean, f: string, g: boolean) => true)
const curried3 = Currying(() => true)

type cases = [
  Expect<Equal<
    typeof curried1,
(a: string) => (b: number) => (c: boolean) => true
  >>,
  Expect<Equal<
    typeof curried2,
(a: string) => (b: number) => (c: boolean) => (d: boolean) => (e: boolean) => (f: string) => (g: boolean) => true
  >>,
  Expect<Equal<typeof curried3, () => true>>,
]

Solution by gearonixx #31782

// 你的答案

type FC = T extends (...arg: infer P) => infer R ? P['length'] extends 0 ? () => R : P extends [first: infer F, ...last: infer L] ? (a: F) => L['length'] extends 0 ? R : FC<(...arg: L) => R> : never : never

declare function Currying(fn: T): FC

Solution by pageword #31438

type Curryed<A extends any[], R> =
  A extends [] ? () => R :
  A extends [infer SingleArg] ? (arg: SingleArg) => R :
  A extends [infer SingleArg, ...infer Rest] ? (arg: SingleArg) => Curryed<Rest, R>
  : never;
declare function Currying<T extends Function>(fn: T): T extends (...args: infer A) => infer R ? Curryed<A,R> : never

一开始我是这样写的

type Curryed<A extends any[], R> =
  A extends [] ? () => R :
  A extends [infer SingleArg] ? (arg: SingleArg) => R :
  A extends [infer SingleArg, ...infer Rest] ? (arg: SingleArg) => Curryed<Rest, R>
  : never;
declare function Currying<A extends any[], R extends any>(fn: (...args: A) => R): Curryed<A,R>

但不知道为啥这样写会把 Currying(() => true) 的参数识别为 () => boolean0

Solution by AEPKILL #31326

type Curried<F> = F extends (...args: infer A) => infer R
  ? A['length'] extends 0 | 1
    ? F
    : A extends [infer HEAD, ...infer TAIL]
      ? (a: HEAD) => Curried<(...args: TAIL) => R>
      : R
  : never

declare function Currying<F>(fn: F): Curried<F>

Solution by Restart20200301 #31158

EZ

// your answers

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

declare function Currying<T extends Function>(fn: T): 
  T extends (...params: infer Params) => infer R 
    ? Params['length'] extends 0 ? () => ReturnType<T>
      : Params['length'] extends 1
        ? (p: Params[0]) => ReturnType<T>
        : (p: Params[0]) => ReturnType<typeof Currying<(...nextFnParams: Shift<Params>) => R>>
    : never;

Solution by kakasoo #30983

type Curry<T extends any[], F> = T extends [infer CURR, ...infer NEXT] ? (arg0: CURR) => Curry<NEXT, F> : F;
declare function Currying<P extends any[], R extends boolean>(fn: (...val: P) => R): Parameters<typeof fn> extends [] ? () => R : Curry<Parameters<typeof fn>, R>;

Solution by ickynavigator #30751

declare function Currying<F>(fn: F): F extends (...args: infer Rest) => infer B ? Rest extends [] ? () => B : CurryResult<Rest,B>  : never;
type CurryResult<Arg,Res> = Arg extends [infer First, ...infer Rest] ?
   (arg: First) => CurryResult<Rest,Res> : Res;

Solution by GrinZero #30618

type CuuryedArgs<Args extends unknown[], Ret> = Args['length'] extends 0
? () => Ret
: Args extends [infer F, ...infer Rest]
? Rest['length'] extends 0 ? (a: F) => Ret : (a: F) => CuuryedArgs<Rest, Ret>
: never;

type CurryedFunc<F extends Function> = F extends (...args: infer Args) => infer Ret
? CuuryedArgs<Args, Ret>
: never;

declare function Currying<F extends Function>(fn: F): CurryedFunc<F>

challenge ts-playgroud

Solution by gegham1 #29445

type SmallerFunction<T> = T extends (p: any, ...q: infer R) => infer S ? (...q: R) => S : any;
type CurryingRecursive<T extends (...p: any[]) => any> =
  Parameters<T>["length"] extends 0 ?
  () => ReturnType<T> :
  Parameters<T>["length"] extends 1 ?
  (p: Parameters<T>[0]) => true :
  (p: Parameters<T>[0]) => CurryingRecursive<SmallerFunction<T>>;
declare function Currying<T>(fn: T): T extends (...p: any[]) => any ? CurryingRecursive<T> : never;

Solution by spotky1004 #29368

This solution is longer but slightly better than the canonical one (#1404) in that it takes care to preserve the names of the function parameters. Extracting the first element of a tuple while preserving its label is tricky but possible thanks to @jcalz's answer to a Stack Overflow question.

// See https://stackoverflow.com/a/72244704/388951
type FirstAsTuple<T extends any[]> = T extends [any, ...infer R]
  ? T extends [...infer F, ...R]
    ? F
    : never
  : never

type Curried<F> = F extends (...args: infer Args) => infer Return
  ? Args['length'] extends 0 | 1
    ? F
    : Args extends [any, ...infer Rest]
    ? (...args: FirstAsTuple<Args>) => Curried<(...rest: Rest) => Return>
    : never
  : never

declare function Currying<T extends Function>(fn: T): Curried<T>

Compare: image

vs: Screenshot 2023-07-05 at 3 00 48 PM

Solution by danvk #28634


type MyCurrying<T> = T extends (...args: infer A) => infer R
  ? A extends [infer F, ...infer L]
    ? L["length"] extends 0
      ? (arg: F) => R
      : (arg: F) => MyCurrying<(...arg: L) => R>
    : () => R
  : T

declare function Currying<F>(fn: F): MyCurrying<F>

Solution by duanbx #28547

type Curried<T, B extends boolean = false> =
  T extends (...args: infer Y) => infer P ? Y extends [infer F, ...infer R]
  ? (...args: [F]) => Curried<(...args: R) => P, true> : B extends true ? P : T : never;
declare function Currying<F>(fn: F): Curried<F>

Solution by smileboyi #27484

type Curry<P, R> = P extends [infer H, ...infer T] ? (p: H) => Curry<T, R> : R

// Extract the args from the generic type, if they're empty just return a function with no arguments
// Else recurisvely go through the arguments and create a function for each
declare function Currying<T extends Function>(fn: T): T extends (...args: infer Args) => infer Return
  ? Args extends never[]
    ? () => Return
    : Curry<Args, Return>
  : never

Solution by dec-land #27238

type GetFunctionParams<T> = T extends (...args: infer R) => unknown ? R : never;
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type GetCurryingResponseFromParams<T extends unknown[], ReturnType, IsEmpty extends boolean> = IsEmpty extends true
  ? () => ReturnType
  : T extends [infer First, ...infer Rest]
  ? (arg: First) => GetCurryingResponseFromParams<Rest, ReturnType, IsEmpty>
  : ReturnType;

declare function Currying<T>(
  fn: T,
): GetCurryingResponseFromParams<
  GetFunctionParams<T>,
  GetReturnType<T>,
  GetFunctionParams<T> extends [] ? true : false
>;

Solution by rldnd #27046

之前提交很多答案都没有考虑函数的参数为空的情况,因此我的代码中有对此进行额外判断,希望能对你有所帮助

Many of the answers submitted previously did not consider the case where the function's argument is empty. So my code has an additional determination for this.  Hope this helps you.

declare function Currying<T>(fn: T): CurryingReturnTypeEnter<T>

type CurryingReturnTypeEnter<T> = T extends (...params: infer Params) => any
  ? Params['length'] extends 0
    ? T
    : CurryingReturnType<T>
  : never

type CurryingReturnType<T> = T extends (...params: infer Params) => infer Result
  ? Params extends [infer First, ...infer Other]
    ? (param: First) => CurryingReturnType<(...params: Other) => Result>
    : Result
  : never

Solution by CwRv07 #26700

type Unshift<T extends any[]> = T extends [infer K, ...infer U] ? U : []
type Curried<Fn> = 
Fn extends (...args: infer Args) => infer Res
  ? Args['length'] extends 0 | 1
    ? (...args: Args) => Res
    : (arg: Args[0]) => Curried<(...args: Unshift<Args>) => Res>
  : never
declare function Currying<Fn>(fn: Fn): Curried<Fn>

Solution by WisestCoder #26468

// your answers
type Curried<T extends any[], R> = T extends [infer F, ...infer Rest]
  ? (arg: F) => Curried<Rest, R>
  : true;

declare function Currying<T extends any[], R>(
  fn: (...args: T) => R
): T["length"] extends 0 ? (...arg: T) => true : Curried<T, R>;

test playground

Solution by DvYinPo #26415

// your answers
declare function Currying<F>(fn: F): Curried<F>
type Curried<F> = F extends (args:never) => any ? F :
    F extends (...args: infer A) => infer R
    ? A extends [infer First, ...infer Other]
        ? (arg: First) => Curried<(...args: Other) => R>
       : R
    : never;

Solution by zry666333 #26322

type ArrayToCurrying<T extends any[], Return> =
  T extends [infer First, ...infer Rest]
  ? (x: First) => Rest extends never
    ?  Return
    : ArrayToCurrying<Rest, Return>
  : Return

declare function Currying<T extends Function>(fn: T): T extends (...args: infer Args) => infer Return
  ? Args extends never[]
    ? () => Return
    : ArrayToCurrying<Args, Return> 
  : never

Solution by Jicmou #25625

// It's possible to write it more concisely. It my first approach without cleaning the code.

type C<Head, Tail, ReturnType> = Tail extends []
  ? (Head: Head) => ReturnType
  : Tail extends [infer THeadArray, ...(infer TTailArray)]
  ? (propertyName: Head) => C<THeadArray, TTailArray, ReturnType>
  : ReturnType;

declare function Currying<TFnArgs>(
  fn: TFnArgs,
): TFnArgs extends () => infer TFnReturnType
  ? () => TFnReturnType
  : TFnArgs extends (...args: infer TFnArgsArray) => infer TFnReturnType
  ? TFnArgsArray extends [infer THeadArray, ...(infer TTailArray)]
    ? C<THeadArray, TTailArray, TFnReturnType>
    : never
  : never;

Solution by jakubjereczek #25157

// your answers
type Currying<T> = T extends (...args: [infer Left, ...infer Rest]) => infer R
    ? Rest['length'] extends 0
        ? T
        : (x: Left) => Currying<(...args: Rest) => R>
    : never
declare function Currying<T extends Function>(fn: T): Currying<T>

Solution by studymachiney #24953

type Currying<T extends Function> = T extends (...args: infer P) => infer R ?
  P extends [infer PF, infer PS, ...infer PR] ? (arg: PF) => Currying<(...args: [PS, ...PR]) => R> : T :
  never;

declare function Currying<T extends Function>(fn: T): Currying<T>;

// old way
// type Currying<T> = T extends (...args: [infer F, ...infer R]) => infer RT ?
//   (R extends [] ? T :
//     (arg: F) => Currying<(...args: R) => RT>) :
//   never;

// declare function Currying<T>(fn: T): Currying<T>

Solution by E-uler #24574

type Curry<F> = F extends (...args: infer Args) => infer R 
  ? Args extends [infer F1,...infer R1] 
    ? (arg:F1) => R1 extends [] ? R: Curry<(...args:R1) => R>
    : F
  : never

declare function Currying<F>(fn: F): Curry<F>;

Solution by songhuige #24404

// 你的答案
type CurryingFn<Fn extends Function> = Fn extends (first:infer F,...args:infer O)=>infer R 
// 参数为空
? O['length'] extends 0
  ? Fn
  : (first:F)=> CurryingFn<(...args:O)=>R>
: never

Solution by walker-hzx #24283

// 你的答案

**第1种解法**
`typescript
declare function Currying<T>(fn: T): Fn<T>;
type Fn<T> = T extends (...args: infer Args) => infer R
  ? Args extends [infer P0, infer P1, ...infer P99]
    ? (p: P0) => Fn<(p: P1, ...p99: P99) => R>
    : T
  : never;
`

**第2种解法(第1种微变写法)**
`typescript
declare function Currying<T extends (...args: any) => any>(fn: T): Fn2<T>;
type Fn2<T extends (...args: any) => any> = Parameters<T> extends [infer P0,infer P1,...infer P99]
  ? (p: P0) => Fn2<(p: P1, ...p99: P99) => ReturnType<T>>
  : T;
`

Solution by Lester1688 #23458

// your answers

type Curry<Args extends unknown[], R> =
  Args extends [infer A, ...infer Rest]
    ? (a: A) => Curry<Rest, R>
    : R

declare function Currying<Fn> (
  fn: Fn,
): Fn extends (...args: infer Args) => infer R
  ? Args extends []
    ? () => R
    : Curry<Args, R>
  : never

Solution by snakeUni #23383