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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discriminated union types lose their narrowed type inside nested function scopes #9258

Closed
ryanatkn opened this issue Jun 19, 2016 · 2 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@ryanatkn
Copy link

TypeScript Version:

nightly (1.9.0-dev.20160619-1.0)

Code

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Rectangle | Circle;

function test1(s: Shape) {
    if (s.kind === "rectangle") {
        s;  // Rectangle
        setTimeout(() => {
          s; // Rectangle | Circle
        });
    }
}

Expected behavior: both s usages to narrow to type Rectangle

Actual behavior: the s in the nested function scope has type Rectangle | Circle

@yortus
Copy link
Contributor

yortus commented Jun 20, 2016

I believe this is by design, and applies to all type guards, not just those for discriminated unions. The problem is that the compiler cannot in general be sure when the nested function will be called, so it's not safe to assume the narrowed type will still apply by the time the function is actually called. The variable may have been re-assigned by then.

There are a couple of special cases involving IIFEs and consts where the narrowing can safely cross into the nested function body, and the compiler handles these cases correctly. See #8849.

@ryanatkn
Copy link
Author

ryanatkn commented Jun 20, 2016

Makes sense, thank you!

So the workaround would look like this:

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Rectangle | Circle;

function test1(s: Shape) {
    if (s.kind === "rectangle") {
        const rect = s;
        s;  // Rectangle
        setTimeout(() => {
          s; // Rectangle | Circle
          rect; // Rectangle
        });
    }
}

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jun 20, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants