泛型函数子类型约束错误和混乱

时间:2019-10-02 22:08:01

标签: typescript generics types subtyping

我一直在考虑使用TypeScript进行简单包装的想法。我最终得到了一些可用于以下方面的东西:

export function logFn<T extends (...args: any[]) => any>(
  fn: T,
): (...args: Parameters<T>) => ReturnType<T>  {
  const log = (...args: Parameters<T>): ReturnType<T> => {
    console.log('log')
    return fn(...args)
  }
  return log
}

这有效并且编译器很高兴。我的问题是关于我的最初尝试,看起来更像这样

export function logFn<T extends (...args: any[]) => any>(
  fn: T,
): T  {
  const log: T = (...args) => {
    console.log('log')
    return fn(...args)
  }
  return log
}

这在log变量声明中给我一个错误,错误是:

Type '(...args: any[]) => any' is not assignable to type 'T'.
  '(...args: any[]) => any' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '(...args: any[]) => any'.

似乎有一些子类型关系约束,我无法完全解决(这个错误也不能给我太多帮助)。希望有人对这方面的知识有更好的掌握,可以给我一个体面的解释,这样我就可以减少对这种行为的迷惑(我确信这是正确的)。

1 个答案:

答案 0 :(得分:2)

“问题”是这样的:T extends (...args: any[]) => any

如果您看到以下版本,它将更加可视化:

export function logFn<T extends (...args: any[]) => any>(fn: T): T {
  return (a, b) => fn(a, b);
}

您将看到错误成立

  

类型'(a:任何,b:任何)=>任何'都不能分配给类型'T'。     '(a:any,b:any)=> any'可分配给类型'T'的约束,但是'T'可以用约束的另一子类型实例化'(... args:any [])= >任何”。

T extends (...args: any[]) => any范围内,以下两项均有效

  1. logFn((a, b) => a + b)
  2. logFn((a, b, c) => c)

但是,如果您再参考我给出的示例,其内部定义为:

return (a, b) => fn(a, b);

所以选项2会在此处引发错误,这就是为什么打字稿会警告您。

logFn<T extends (...args: any[]) => any>(fn: T): T

我们将收到类型T并返回类型Treturn (a, b) => fn(a, b);是有效的返回类型,它确实扩展了(...args: any[]) => any,但是如何确定传递到fnT)的值与该签名匹配呢? (即它可能是(...args: any[]) => any的另一个不兼容的子类型)

不确定我的解释是否足够好,但这就是我的理解

您的解决方法很好的原因是,通过添加(...args: Parameters<T>) => ReturnType<T>告诉编译器,参数和返回类型必须与传递的函数匹配,而不是T,后者可以是任何其他函数定义