TypeScript
TypeScript copied to clipboard
Bizarre circular type false positive
Bug Report
🔎 Search Terms
implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.(7022)
🕗 Version & Regression Information
This changed between versions 4.6.2 and 4.7.0-beta
⏯ Playground Link
Playground link with relevant code
💻 Code
type Wrapper = {
data: number | null;
};
function example(wrapper: Wrapper) {
// The error goes away if this line is removed and `null` is removed from the type
if (wrapper.data === null) return;
// The error goes away if the loop body is inlined o_O
for (const _ of []) {
const d = wrapper.data;
// ^ 'd' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
// The error goes away without these lines o_O
const t = d;
const list = [0];
if (list[t]) {}
}
}
🙁 Actual behavior
Error: implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer
🙂 Expected behavior
No error
cc @ahejlsberg since you made some significant type flow analysis changes
:wave: Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript.
Issue body code block by @MichaelMitchell-at
:x: Failed: -
'd' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
Historical Information
Version | Reproduction Outputs |
---|---|
4.2.2, 4.3.2, 4.4.2, 4.5.2, 4.6.2 |
:+1: Compiled |
The change between 4.6.2 and Nightly occurred at 162713fac9da0304769ef0b7cfdd568f319a55ea.
Have a similar repro; was looking to file a bug and this seems like the right place for it.
interface A {
a?: A
}
function foo(a: A) : void {
let working : A | undefined = a;
while (working) {
// 'next_A' has type 'any' because it is referenced in its own initializer
const next_A = working.a;
if (next_A) {
working = next_A;
}
}
}
I looked a bit into what part of https://github.com/microsoft/TypeScript/commit/162713fac9da0304769ef0b7cfdd568f319a55ea caused this regression. It seems like adding isEntityNameExpression(expr.argumentExpression)
to isNarrowableReference
allows flowNode
s to be set on expressions where they weren't previously during the binding phase. This in turn seems to lead to a circular type resolution when typing the expression along the getFlowTypeOfReference
code path.
I haven't looked deep enough yet to understand why the circular type resolution happens yet, but I'm sharing my findings here in case it helps someone else pick up this investigation.
@Andarist you've been pretty capable at fixing random bugs I've stumbled upon before, so maybe this one would be of interest to you 😉
The error also goes away if you simply move just the initialization out of the loop:
type Wrapper = {
readonly data: number | null;
};
function example(wrapper: Wrapper) {
if (wrapper.data === null) return;
const d = wrapper.data;
for (const _ of []) {
const t = d;
const list = [0];
if (list[t]) {}
}
}
Also, per the Playground link, the change happened after 4.6.4. I presume https://github.com/microsoft/TypeScript/commit/162713fac9da0304769ef0b7cfdd568f319a55ea is post 4.6.4?