Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

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 opened this issue Apr 26, 2022 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@dead-claudia
Copy link

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.

@MartinJohns
Copy link
Contributor

The assignments to state in all examples to fail, as number is not assignable to the enum State

But number is assignable to the enum State. This is intentional: #48296

@andrewbranch andrewbranch added the Duplicate An existing issue was already created label Apr 26, 2022
@andrewbranch
Copy link
Member

I think this is the same issue as pretty much all the ones you linked.

@dead-claudia
Copy link
Author

dead-claudia commented Apr 27, 2022

@andrewbranch In that the detected circularity doesn't stop at intermediate calls or array accesses? Does seem odd that the array access triggers it even though the type it'd be checking assignability to is [key: number].

Edit: Also, it's worth noting the circularity error disappears when you use it directly. I would at least expect consistent behavior across let-renaming, and that's probably the core of this bug report.

@andrewbranch
Copy link
Member

Also, it's worth noting the circularity error disappears when you use it directly. I would at least expect consistent behavior across let-renaming, and that's probably the core of this bug report.

That鈥檚 also a feature of #43047.

It is a little puzzling that there鈥檚 a difference between the array indexing and the lookup2 call, but I think that鈥檚 a minor detail in a big picture of undesirable behavior. From my perspective, it鈥檚 not as if we鈥檙e happy with this error in all the related issues but one small permutation would make us consider it a must-fix bug. It all looks bad, it鈥檚 not shocking that it manifests in slightly different ways, and it鈥檚 likely all difficult to improve.

@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants