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.
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!
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 ofarg.arr
. That in turn means we need to look at constructs that affect the type ofarg
. This includes the call tootherFunc
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 ofITEM
. This creates a circularity we can't resolve, so we fall back to an implicitany
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 tootherFunc
. In other words, change the call tootherFunc(ITEM, (arg))
. This causes the control flow analyzer to no longer consider the call a possible user defined type predicate invocation.