镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

strictNullChecks: false positive with switch in for loop #27239

Description

@AlCalzone

TypeScript Version: 3.1.0-dev.20180920

Search Terms: strictNullChecks

Code
This might seem weird, but is based on actual real-world code.

declare function baz(): Promise<true | "a" | "b" | "c" | Error>;

async function foo() {
    let bar: "x" | Error;
    for (let i = 0; i < 3; i++) {
        const test = await baz();
        switch (test) {
            case true: return;
            case "a": throw new Error("a");
            case "b": {
                bar = "x";
                continue;
            }
            default: {
                if (test instanceof Error) {
                    bar = new Error("foo");
                    continue;
                } else {
                    throw new Error("c");
                }
            }
        }
    }
    // false positive: bar is used before being assigned
    if (bar === "x") {

    } else {
        bar.message = "something";
    }
}

Expected behavior:
No error after the for loop. The only way to get there is if bar has the value "x" or is an instance of Error. If baz resolves to true, the function returns, "b" sets bar to "x", and "a" and everything that is not an Error (including "c" throw, and an Error sets it to an Error.

Actual behavior:
Variable "bar" is used before being assigned.

Playground Link: https://www.typescriptlang.org/play/#src=declare%20function%20baz()%3A%20Promise%3Ctrue%20%7C%20%22a%22%20%7C%20%22b%22%20%7C%20%22c%22%20%7C%20Error%3E%3B%0D%0A%0D%0Aasync%20function%20foo()%20%7B%0D%0A%20%20%20%20let%20bar%3A%20%22x%22%20%7C%20Error%3B%0D%0A%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%203%3B%20i%2B%2B)%20%7B%0D%0A%20%20%20%20%20%20%20%20const%20test%20%3D%20await%20baz()%3B%0D%0A%20%20%20%20%20%20%20%20switch%20(test)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20case%20true%3A%20return%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20case%20%22a%22%3A%20throw%20new%20Error(%22a%22)%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20case%20%22b%22%3A%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bar%20%3D%20%22x%22%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20default%3A%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(test%20instanceof%20Error)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bar%20%3D%20new%20Error(%22foo%22)%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20Error(%22c%22)%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20if%20(bar%20%3D%3D%3D%20%22x%22)%20%7B%0D%0A%20%20%20%20%20%20%20%20%0D%0A%20%20%20%20%7D%20else%20%7B%0D%0A%20%20%20%20%20%20%20%20bar.message%20%3D%20%22something%22%3B%0D%0A%20%20%20%20%7D%0D%0A%7D

Related Issues: #24091

Activity

  1. AlCalzone commented on Oct 31, 2018

    @AlCalzone
    ContributorAuthor

    We don't check that the for loop is run at least once.

    Would you consider adding a way to assert "I definitely know this for loop will be run at least once"?

  2. AlCalzone commented on Nov 1, 2018

    @AlCalzone
    ContributorAuthor

    Andy (Andrewkraft) (@Andy-MS) It seems I forgot to mention a key point. If I change the condition after the loop to the following

        if (bar! === "x") {
            // bar is "x"
        } else {
            // bar is "x" | Error, should be Error
            bar.message = "something";
       }

    the type for bar in the else branch includes "x", when it should be an Error instance. The analyzer gets it right when I inline the loop body or do this:

        bar = bar!; // <-- gross!
        if (bar === "x") {
            // bar is "x"
        } else {
            // bar is Error
            bar.message = "something";
       }

    What about something like this to tell the compiler to treat the loop body as "inlined"?

    for!(...) { // notice the !
        // ...
    }
  3. typescript-bot commented on Dec 14, 2018

    @typescript-bot
    Contributor

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

  4. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions