Closed
Description
Bug Report
🔎 Search Terms
Just searched the error message noted below.
Possibly related:
- Avoidable circular reference error in types #45805
- Variable modified in loop supposedly "referenced directly or indirectly in its own initializer" #43047
- async, await, loop, implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. #36687
- Incorrect circularity detection #33191
- Fails for simple type inference #32586
- Unexpected implicit any for return value of Array<T>.indexOf #29290
🕗 Version & Regression Information
- This is the behavior in every version I tried
💻 Code
Note: the below does not coerce, but it works the same when coerced with
1 << x
,state + 1
, and similar.
const enum State {
One,
Two,
}
declare const LUT: number[]
declare function lookup1<N extends number>(value: N): number
declare function lookup2(value: number): number
function test1(state: State): void {
if (state === State.Two) return
while (true) {
if (state !== State.One) return
const current = state
state = LUT[current]
}
}
function test2(state: State): void {
if (state === State.Two) return
while (true) {
if (state !== State.One) return
const current = state
state = lookup1(current)
}
}
function test3(state: State): void {
// if (state === State.Two) return
while (true) {
if (state !== State.One) return
const current = state
state = LUT[current]
}
}
function test4(state: State): void {
if (state === State.Two) return
while (true) {
if (state !== State.One) return
state = LUT[state]
}
}
function test5(state: State): void {
if (state === State.Two) return
while (true) {
if (state !== State.One) return
const current = state
state = lookup2(current)
}
}
Output
"use strict";
function test1(state) {
if (state === 1 /* Two */)
return;
while (true) {
if (state !== 0 /* One */)
return;
const current = state;
state = LUT[current];
}
}
function test2(state) {
if (state === 1 /* Two */)
return;
while (true) {
if (state !== 0 /* One */)
return;
const current = state;
state = lookup1(current);
}
}
function test3(state) {
// if (state === State.Two) return
while (true) {
if (state !== 0 /* One */)
return;
const current = state;
state = LUT[current];
}
}
function test4(state) {
if (state === 1 /* Two */)
return;
while (true) {
if (state !== 0 /* One */)
return;
state = LUT[state];
}
}
function test5(state) {
if (state === 1 /* Two */)
return;
while (true) {
if (state !== 0 /* One */)
return;
const current = state;
state = lookup2(current);
}
}
Compiler Options
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2017",
"jsx": "react",
"module": "ESNext",
"moduleResolution": "node"
}
}
Playground Link: Provided
🙁 Actual behavior
current
intest1
andtest2
fail with "'current' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. (7022)"current
in the rest are all resolved toState.One
- No other errors are present
🙂 Expected behavior
current
in all examples to be resolved to theState.One
- The assignments to
state
in all examples to fail, asnumber
is not assignable to the enumState
It's worth noting that Intellisense picks up that state
is contextually State.One
in the RHS of const current = state
in all 5 examples. My suspicion is state
's narrowed type just isn't being propagated to current
, or at least the false circularity prevents it from being propagated - manually specifying the narrowed type does in fact work.