Skip to content

Inferred implicit any from constructor call #10978

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
yortus opened this issue Sep 19, 2016 · 5 comments
Closed

Inferred implicit any from constructor call #10978

yortus opened this issue Sep 19, 2016 · 5 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@yortus
Copy link
Contributor

yortus commented Sep 19, 2016

TypeScript Version: nightly 2.1.0-dev.20160918

Code

// @noImplicitAny: true
class M {
    constructor(fn: () => any) { }
}

const a = new M(() => a); // ERROR: 'a' implicitly has type 'any'...

Expected behavior:

No errors. Inferred type of a is M.

The expression new M(...) always has type M, it does not depend on the argument used in M's constructor.

Actual behavior:

Compiler error as shown in code comment.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Sep 19, 2016
@RyanCavanaugh
Copy link
Member

The thought process in the code basically runs like this:

  • Let's figure out the type of a from its initializer
  • What is the type of new M ?
  • Resolve M to a constructor function
  • Which overload, if any, of the function is applicable?
  • There's one overload, is it applicable?
  • There's a function here, its argument list OK, is its return type OK?
  • That depends, is the type of a an OK return type for the overload under consideration?
  • PANIC, we are trying to see the type of a while resolving a's type!
  • Bail out of the entire typecheck operation for a
  • Had we not bailed out here, eventually we'd contextually type the return expression by the explicit any and remove the implicitness

The issue here is that we can't actually know that it's correct to apply the contextual any return type to the function expression until we know that the M constructor overload is applicable at all. We're not equipped to do this "Had everything not failed, would it have succeeded" kind of logic, unfortunately.

@yortus
Copy link
Contributor Author

yortus commented Sep 20, 2016

Thanks for explaining @RyanCavanaugh. For anyone encountering this, a workaround is to explicitly annotate a, as in:

const a: M = new M(() => a);

In terms of impact, it's more problematic when you are not using --noImplicitAny, then a is typed as any and all sorts of subsequent code is not type-checked. This cropped up due to a typo in our code (the cyclic use of a was unintentional). It took quite a while to find the problem given the lack of compile-time errors. Of course that's also another reason to adopt --noImplicitAny!

@yortus
Copy link
Contributor Author

yortus commented Sep 20, 2016

Followup question -> when the compiler 'bails out' because it detects an infinite type-checking loop, is any really the best type to bail out with? That has the side-effect of reporting no further errors no matter what is done with the variable.

If the compiler bailed out with {}, then at least it would be obvious when trying to use the variable that there was an inference problem and an annotation needs to be added.

@RyanCavanaugh
Copy link
Member

Consider this unannotated code

function fac(n) {
    return n === 1 ? 1 : fac(n - 1) * n;
}

We infer the return type of fac to be any because it references itself in its return expression. If we had said that it was { }:

function fac(n): { } {
    return n === 1 ? 1 : fac(n - 1) * n;
}

Now you get an error that you can't use the * operator on a value of type { }. Same for any other property access -- we'd basically turn all recursive JavaScript functions into errors, which is quite bad.

@yortus
Copy link
Contributor Author

yortus commented Sep 22, 2016

Thanks @RyanCavanaugh, makes sense. There may be some cleverer way of analysing recursive types, but that would presumably involve substantial changes to the compiler. That can be for another issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

2 participants