Skip to content

When one variant of a two-variant enum is narrowed out via control flow both prior to an indefinite loop, attempts to save the narrowed variable to another local result in a circularity error #48849

Closed
@dead-claudia

Description

@dead-claudia

Bug Report

🔎 Search Terms

Just searched the error message noted below.

Possibly related:

🕗 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 in test1 and test2 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 to State.One
  • No other errors are present

🙂 Expected behavior

  • current in all examples to be resolved to the State.One
  • The assignments to state in all examples to fail, as number is not assignable to the enum State

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions