【问题标题】:How to write generic partialCall function which consume tail parameters?如何编写使用尾参数的通用 partialCall 函数?
【发布时间】:2022-01-10 09:06:44
【问题描述】:

variadic types 中的 partialCall 示例“使用”参数列表头部的参数。

我正在实现一个版本,其中参数从参数列表的尾部使用,但编译器在推断类型时遇到问题。

有没有办法改进这一点,使编译器可以正确推断类型?

type Arr = readonly unknown[];

function partialCallTail<T extends Arr, U extends Arr, R>(
  f: (...args: [...T, ...U]) => R, ...tailArgs: U
) {
  return (...headArgs: T) => f(...headArgs, ...tailArgs)
}

const foo = (x: string, y: number, z: boolean) => {}

// compiles, but verbose
const foo2: (x: string, y: number) => void = partialCallTail(foo, false)
foo2('a', 2)

const foo1: (x: string) => void = partialCallTail(foo, 2, false)
foo1('a')

// does not compile
partialCallTail(foo, false)('a', 2)
                ~~~

TS2345: Argument of type '(x: string, y: number, z: boolean) => void' is not assignable to parameter of type '(...args: [...unknown[], boolean]) => void'.
   Types of parameters 'x' and 'args_0' are incompatible.
     Type '[...unknown[], boolean]' is not assignable to type '[x: string, y: number, z: boolean]'.
       Target requires 3 element(s) but source may have fewer.

Playground 在下方发表评论。

【问题讨论】:

    标签: typescript generics


    【解决方案1】:

    可能有更简单的方法,但这里有一个工作版本:

    type Arr = readonly unknown[];
    
    type Func = (...args: any[]) => any
    
    type Head<Args extends Arr, Tail extends Arr> = Args extends [...infer Head, ...Tail] ? Head : never;
    
    type Tail<Args extends Arr> = Args extends [unknown, ...infer Rest]
      ? Rest extends [] ? never
      : Rest | Tail<Rest> : never
    
    function partialCallTail<F extends Func, TailArgs extends Tail<Parameters<F>>>(
      f: F, ...tailArgs: TailArgs
    ) {
      return (...headArgs: Head<Parameters<F>, TailArgs>) => f(...headArgs, ...tailArgs)
    }
    
    const foo = (x: string, y: number, z: boolean) => {}
    
    partialCallTail(foo, false)('a', 2)
    
    partialCallTail(foo, 2, false)('a')
    
    // Expected error
    partialCallTail(foo, 2, false)(1)
    

    Playground

    主要区别在于我们从推断传递的函数类型开始,然后得到可能的尾部联合。


    如果您出于某种原因(不再是部分参数)允许不提供/所有参数,只需更改 Tail

    type Tail<Args> = Args extends [unknown, ...infer Rest]
      ? Args | Tail<Rest> : Args
    

    Playground

    【讨论】:

    • 谢谢。不幸的是,虽然这使编译器对这些简单的示例感到满意,但一旦我开始将它与Array.filter 一起使用,运行时就会出现问题。我将使用游乐场链接更新问题。
    • @Matt 我添加了示例如何允许使用所有参数或不使用参数进行调用。关于filter - 这就是它的工作原理,它总是会传递所有参数,回调可以选择是否使用它们。 Javascript 中没有重载,类型定义在编译时被删除并且对运行时行为没有影响。
    猜你喜欢
    • 2018-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多