可以使用其他约束子类型实例化函数

时间:2019-12-02 23:35:43

标签: typescript

TypeScript拒绝编译debounce函数,因为包装函数的类型有问题:

export function debounce<F extends ((...args: any[]) => void)>(fn: F, timeout: number): F {
  let timer: NodeJS.Timeout | undefined

  // Problem here, TypeScript complains it's not the same function as F
  return ((...args: any[]) => {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => fn(...args), timeout)
  })
}

错误:

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

该如何解决?没有强制类型转换return ... as Freturn ... as any

1 个答案:

答案 0 :(得分:3)

问题在于,(...args: any[]) => void上的约束F可以满足许多您可能会感到惊讶的类型的要求,并且您返回的函数将无法分配给此类类型。例如:

debounce(() => "oopsie", 1000)().toUpperCase(); // okay at compile time, typeError at runtime

此处,函数类型F返回一个string值;这可以分配给void返回函数类型,例如explained in the FAQ。但是当然debounce()不会返回返回string的函数,因此debounce()的返回类型与传入的F不同。

也:

function foo() { };
foo.prop = 123; // property declaration
debounce(foo, 1000).prop.toFixed(); // okay at compile time, TypeError at runtime

在这种情况下,我们有一个带有property declared on it的函数。因此,这里的F类型将是具有附加()=>void属性的函数类型prop。但是同样,debounce()不会返回带有此额外prop属性的函数,因此debounce()的返回类型与传入的F也不相同。< / p>


这里的解决方法是使debounce()仅通用到足以代表您实际在做什么。返回的函数将采用与传入函数相同的参数列表,因此我们需要参数列表是通用的。并且返回的函数肯定会返回void,并且不会在其上具有额外的属性。因此,只有参数列表需要类型参数(例如A),输入和输出函数的类型均为(...args: A) => void

export function debounce<A extends any[]>(
    fn: (...args: A) => void,
    timeout: number
): (...args: A) => void {
    let timer: NodeJS.Timeout | undefined

    return ((...args: A) => {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => fn(...args), timeout)
    })
}

该编译没有错误。好的,希望能有所帮助;祝你好运!

Link to code