4

在 TypeScript 中,如果将函数表达式作为参数传递,则可以完美推断其参数的类型:

var foo = (fn: (a: string) => void) => {};

foo(a => { 
    // a is inferred to be a string
    alert(a.toLowerCase());
});

这对于事件处理程序和其他回调非常方便。但是,如果函数表达式被包装在通用包装函数的调用中,该函数将函数作为参数并返回具有相同签名(返回值除外)的函数,例如_.debounceLodash,则不会发生推理。

var debounce = <T>(fn: (a: T) => void) => {
    return (a: T) => { /* ... */ };
};

foo(debounce(a => { 
    // a is inferred to be {}
    // type error: 'toLowerCase' doesn't exist on '{}'
    alert(a.toLowerCase());
}));

TS游乐场

由于foo希望它的参数是(a: string) => void,编译器可以尝试找到这样的类型,因为Tdebounce<T>会返回(a: string) => void。但它不会尝试这样做。

我错过了什么吗?我应该debounce以其他方式编写类型注释吗?是设计使然吗?GitHub上有关于这个案例的问题吗?

UPDATE-2017:现在可以使用了!TS 2.5。

4

1 回答 1

5

首先,当您调用没有类型参数的泛型函数时,编译器必须确定每个类型参数的类型。

因此,当您调用时debounce,编译器必须为T. 但是唯一debounce可以从中得出推论的地方是您的函数,并且您没有为您的类型参数提供明确的类型。

所以编译器认为它没有任何类型可以使用,并回退到{}(“空类型”,通常称为“curly curly”)T

整个过程称为类型参数推断

然后发生的事情是编译器会注意到您没有为箭头函数的参数指定类型。它不是默认为any,而是认为它可以从 的参数类型中找出它debounce。这个过程称为上下文类型

Wellfn的类型基本上是(a: {}) => void,所以编译器认为“好吧,太好了,我们可以给出a一个类型!” 结果是……{}不幸的是。


因此,虽然这不是绝对很棒,但修复并没有那么糟糕。只需添加一个类型注释a

foo(debounce((a: string) => { 
    // Everything is fine!
    alert(a.toLowerCase());
}));

或使用类型参数:

foo(debounce<string>(a => { 
    // Everything is fine!
    alert(a.toLowerCase());
}));
于 2016-02-25T08:59:14.180 回答