semlinker/awesome-typescript

「重学TS 2.0 」TS 练习题第二十题

Opened this issue · 16 comments

实现一个 Curry 工具类型,用来实现函数类型的柯里化处理。具体的使用示例如下所示:

type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>, 
  R = ReturnType<F> 
> = // 你的实现代码

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg_0: number) => (b: string) => Date

请在下面评论你的答案。

//求指点感觉不是很对

type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>,
  R = ReturnType<F>
> = P extends [infer A, ...infer B]
  ? B extends []
    ? (...args: [A]) => R
    : (...args: [A]) => Curry<(...args: B) => R>
  : () => R;
//
type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg_0: number) => (b: string) => Date
// 实现一个 Curry 工具类型,用来实现函数类型的柯里化处理。具体的使用示例如下所示:

type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>,
  R = ReturnType<F>,
> = P extends [infer A, ...infer B]
  ? B extends []
    ? (arg: A) => R
    : (arg: A) => Curry<(...args: B) => R>
  : R;

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg_0: number) => (b: string) => Date
Honye commented
type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>, 
  R = ReturnType<F> 
> = P extends [infer A, infer B, ...infer C]
  ? (arg: A) => Curry<(...args: [B, ...C]) => R>
  : F;

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (a: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; // (arg: number) => (args_0: string) => Date

加强版本,保留原参数名

type FirstAsArray<T extends any[]> = 
T extends [...infer A, infer B, infer C] 
  ? A extends []
    ? T extends [...infer A, infer B] ? A : never
    : T extends [...infer A, infer B] ? FirstAsArray<A> : never
  : T;

type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>,
  R = ReturnType<F> 
> = P extends [infer A, infer B, ...infer C]
  ? P extends [infer A, ...infer B]
    ? Curry<F, FirstAsArray<P>, Curry<F, B, R>>
    : never
  : (...args: P) => R;

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (a: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; // (a: number) => (b: string) => Date
type F3 = Curry<(a: number, b: string, c: boolean) => Date> // (a: number) => (b: string) => (c: boolean) => Date
ln0y commented

保留参数名

  type FirstAsArray<T extends any[]> = T extends [...infer A, infer B] ? A extends [] ? T : FirstAsArray<A> : []
  type Curry<
    F extends (...args: any[]) => any,
    P extends any[] = Parameters<F>,
    R = ReturnType<F>
  > = P extends [infer A, ...infer B] ? B extends [] ? F : (...arg: FirstAsArray<P>) => Curry<(...arg: B) => R> : F

  type LastAsArray<T extends any[]> = T extends [infer A, ...infer B] ? B extends [] ? T : LastAsArray<B> : []
  type Curry<
    F extends (...args: any[]) => any,
    P extends any[] = Parameters<F>,
    R = ReturnType<F>
    > = P extends [...infer A, infer B] ? A extends [] ? F : Curry<(...args: A) => (...arg: LastAsArray<P>) => R> : F

  type F0 = Curry<() => Date> // () => Date
  type F1 = Curry<(a: number) => Date> // (arg: number) => Date
  type F2 = Curry<(a: number, b: string) => Date> //  (arg_0: number) => (b: string) => Date
  type F3 = Curry<(a: number, b: string, c: boolean) => Date> // (a: number) => (b: string) => (c: boolean) => Date
// 实现一个 Curry 工具类型,用来实现函数类型的柯里化处理。具体的使用示例如下所示:

type Curry<
F extends (...args: any[]) => any,
P extends any[] = Parameters<F>,
R = ReturnType<F>
> = P extends [infer A, ...infer B]
  ? B["length"] extends 0
    ? F
    : (arg: A) => Curry<(...args: B) => R>
  : F;

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg_0: number) => (b: string) => Date

解题思路: 没啥特别要说的,依然是利用 extends 来探测参数的长度,利用递归逐渐减少参数个数。算法**是是减治。

Mrlgm commented
type Curry<
    F extends (...args: any[]) => any,
    P extends any[] = Parameters<F>,
    R = ReturnType<F>
    > = P extends [infer U, ...infer Arg] ? (arg: U) => Curry<F, Arg, R> : P extends [] ? () => R : never

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg_0: number) => (b: string) => Date
type Curry<F extends ((...args: any[]) => any)> = F extends ((arg_0: infer Arg, ...restArg: infer Rests) => any) ? (
    Rests extends [] ? F : ((arg: Arg) => Curry<(...args: Rests) => ReturnType<F>>)
) : never

type F0 = Curry<() => Date> // () => Date
type F1 = Curry<(a: number) => Date> // (a: number) => Date
type F2 = Curry<(a: number, b: string) => Date> // (a: number) => (b: string) => Date

//求指点感觉不是很对

type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>,
  R = ReturnType<F>
> = P extends [infer A, ...infer B]
  ? B extends []
    ? (...args: [A]) => R
    : (...args: [A]) => Curry<(...args: B) => R>
  : () => R;
//
type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg_0: number) => (b: string) => Date

A 不需要用 [] 包起来

type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>, 
  R = ReturnType<F> 
> = P extends [infer F, ...infer Rest] ?
      Rest extends [] ? 
      (arg:F) => R
      : (arg:F) => Curry<(...args: Rest) => R>
    : () => R
    

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg: number) => (arg: string) => Date
type F3 = Curry<(a: number, b: string, c: boolean) => Date>; //  (arg: number) => (arg: string) => (arg: boolean) => Date

柯里化 是啥意思

type Curry<F extends (...any: any[]) => any, A extends any[] = Parameters<F>> = 
    A extends [one: infer One, ...rest: infer Rest]
        ? A extends [one: any]
            ? F
            : (a: One) => Curry<(...arg: Rest) => ReturnType<F>>
        : F
type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>,
  R = ReturnType<F>
> = P extends [infer A, ...infer B]
  ? (arg: A) => B[number] extends never ? R : Curry<(arg: B[number]) => R>
  : () => R

保留参数名

  type FirstAsArray<T extends any[]> = T extends [...infer A, infer B] ? A extends [] ? T : FirstAsArray<A> : []
  type Curry<
    F extends (...args: any[]) => any,
    P extends any[] = Parameters<F>,
    R = ReturnType<F>
  > = P extends [infer A, ...infer B] ? B extends [] ? F : (...arg: FirstAsArray<P>) => Curry<(...arg: B) => R> : F

  type LastAsArray<T extends any[]> = T extends [infer A, ...infer B] ? B extends [] ? T : LastAsArray<B> : []
  type Curry<
    F extends (...args: any[]) => any,
    P extends any[] = Parameters<F>,
    R = ReturnType<F>
    > = P extends [...infer A, infer B] ? A extends [] ? F : Curry<(...args: A) => (...arg: LastAsArray<P>) => R> : F

  type F0 = Curry<() => Date> // () => Date
  type F1 = Curry<(a: number) => Date> // (arg: number) => Date
  type F2 = Curry<(a: number, b: string) => Date> //  (arg_0: number) => (b: string) => Date
  type F3 = Curry<(a: number, b: string, c: boolean) => Date> // (a: number) => (b: string) => (c: boolean) => Date

这个为什么可以保留参数名啊

type Curry<
  F extends (...args: any[]) => any,
  P extends any[] = Parameters<F>, 
  R = ReturnType<F> 
> = P extends [infer First, ...infer Rest] ?
      Rest extends [] ?
        F
        :
        (...args: [First]) => Curry<(...args: Rest) => R>
      :F

柯里化 是啥意思

函数返函数
() => () => () => ''

type Curry<
F extends (...args: any[]) => any,
P extends any[] = Parameters,
R = ReturnType
> = P extends [infer P0, ...infer P1] ? (arg: P0) => P['length'] extends 1 ? R : Curry<((...args:P1)=>R)> : F;

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; // (arg_0: number) => (b: string) => Date