microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.94k stars 12.47k forks source link

Generic function inference fails when using a delegate function parameter that only uses generic types #5249

Closed patsissons closed 9 years ago

patsissons commented 9 years ago

The title probably sounds a bit verbose, but this is a bit of a complicated issue to describe. It's best if I paste some code and explain it.

protected Test1<T>(val1: T, sel: (T) => T) {
    return sel(val1);
}

protected Test2<T>(val1: T, sel: (x: T) => T) {
    return sel(val1);
}

protected Test3() {
    let a = this.Test1(123, x => x + 123);
    let b = this.Test2(123, x => x + 123);
}

both Test1 and Test2 perform the exact same function. But in Test3 a will be typeless (any) and b will be strongly typed to number. The only difference is that Test2 uses a delegate parameter instance variable in the declaration (x). Now that I have found a work around this is less of an issue, but others may want to know about this because it's super annoying writing projector functions that don't return strongly typed results.

RyanCavanaugh commented 9 years ago

This declaration:

   protected Test1<T>(val1: T, sel: (T) => T) {

is equivalent to this declaration:

   protected Test1<T>(val1: T, sel: (T_The_Parameter_Name: any) => T) {

The parameter name T has no relation to the generic type parameter T. So we should expect that a will be any because the type parameter T got two inference candidates: number (via val1) and any (via the return type of the function body, where we computed the return expression of the form any + number to be of type any), between which any is chosen because it is more general.

You may be interested in the --noImplicitAny compiler flag, or the discussion in #5187 and #3081.

patsissons commented 9 years ago

Thank you for the explanation, the issue now makes much more sense. I will turn on the flag in my project. I think it definitely makes sense to enable that flag by default to make it an opt out.

I'll close this issue now since it is by design.