Typescript: Type checker loses type in for-of

Created on 8 Nov 2016  ·  3Comments  ·  Source: microsoft/TypeScript

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.
Bug Fixed

Most helpful comment

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.

All 3 comments

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
Thank you for the parentheses workaround.
I want to figure this out. PR is acceptable, isn't it?

@arusakov Fix now in master!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

kyasbal-1994 picture kyasbal-1994  ·  3Comments

Roam-Cooper picture Roam-Cooper  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments