Skip to content
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

Type checker loses type in for-of #12101

Closed
arusakov opened this issue Nov 8, 2016 · 3 comments
Closed

Type checker loses type in for-of #12101

arusakov opened this issue Nov 8, 2016 · 3 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@arusakov
Copy link
Contributor

arusakov commented Nov 8, 2016

TypeScript Version: 2.0.8

Code

// test.ts
interface Type {
  type: number;
}

interface TypeExt extends Type {
  arr: Type[];
}

const guard = (arg: Type): arg is TypeExt => arg.type === 1;
const otherFunc = (arg1: Type, arg2: TypeExt): void => {};

export function y(arg: Type): void {
  if (guard(arg)) {
    for (const ITEM/* error is here */ of arg.arr) {
      if (otherFunc(ITEM, arg)) {
      }
    }
  }
}

Compiled with cmd: tsc --noImplicitAny test.ts

Expected behavior:
No errors

Actual behavior:

test.ts(14,16): error TS7022: 'ITEM' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
@ahejlsberg
Copy link
Member

This is a control flow analysis issue. In order to infer a type for ITEM we need to figure out the control flow type of arg.arr. That in turn means we need to look at constructs that affect the type of arg. This includes the call to otherFunc which looks like it could be a user defined type predicate. In order to determine if it is a type predicate, we resolve the call expression, which entails resolving the argument expressions, which requires us to know the type of ITEM. This creates a circularity we can't resolve, so we fall back to an implicit any type.

We can fix this by having the type predicate recognition logic first resolve the type of the function object and examine all of the call signatures. If none of them are user defined type predicates, we can bail out early without resolving the call argument expressions, which avoids the circularity.

Meanwhile, you can break the circularity simply by adding parentheses to arg in the call to otherFunc. In other words, change the call to otherFunc(ITEM, (arg)). This causes the control flow analyzer to no longer consider the call a possible user defined type predicate invocation.

@ahejlsberg ahejlsberg added Bug A bug in TypeScript Design Limitation Constraints of the existing architecture prevent this from being fixed and removed Design Limitation Constraints of the existing architecture prevent this from being fixed labels Nov 8, 2016
@arusakov
Copy link
Contributor Author

arusakov commented Nov 9, 2016

@ahejlsberg
Thank you for the parentheses workaround.
I want to figure this out. PR is acceptable, isn't it?

@ahejlsberg ahejlsberg self-assigned this Nov 13, 2016
@mhegazy mhegazy added this to the TypeScript 2.1.3 milestone Nov 13, 2016
@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Nov 13, 2016
@ahejlsberg
Copy link
Member

@arusakov Fix now in master!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

3 participants