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

Strange boolean-discriminant narrowing with strictNullChecks off #10564

Open
gcnew opened this issue Aug 26, 2016 · 7 comments · May be fixed by #55291
Open

Strange boolean-discriminant narrowing with strictNullChecks off #10564

gcnew opened this issue Aug 26, 2016 · 7 comments · May be fixed by #55291
Labels
Bug A bug in TypeScript Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Help Wanted You can do this
Milestone

Comments

@gcnew
Copy link
Contributor

gcnew commented Aug 26, 2016

TypeScript Version: nightly (2.1.0-dev.20160826)

Code

type Result = { success: true }
            | { success: false, error: string }

function handleError(res: Result) {
    if (res.success === true) {
        return;
    }

    res.error; // OK
}

function handleError2(res: Result) {
    if (res.success !== false) {
        return;
    }

    res.error; // OK
}

function handleError3(res: Result) {
    if (res.success) {
        return;
    }

    res.error; // Property 'error' does not exist on type 'Result'
               // but should be OK
}

Expected behavior:
All three cases should behave the same.

@RyanCavanaugh
Copy link
Member

This is correct. We don't know that res.success isn't undefined or null, which would mean it's possibly not false and therefore not { success: false, error: string }

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Aug 26, 2016
@gcnew
Copy link
Contributor Author

gcnew commented Aug 26, 2016

@RyanCavanaugh While you are technically right (and I was thinking the same), when strictNullChecks is off null and undefined are not modelled at all and such differences don't surface in other situations. For example handleError has exactly the same semantics but compiles just fine. I think the behaviour should be consistent or at least the error message should be more descriptive.

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript and removed Working as Intended The behavior described is the intended behavior; this is not a bug labels Aug 26, 2016
@mhegazy mhegazy added this to the TypeScript 2.1 milestone Sep 21, 2016
@mhegazy mhegazy modified the milestones: Future, TypeScript 2.1 Sep 29, 2016
@DanielRosenwasser DanielRosenwasser added Help Wanted You can do this Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". labels Sep 13, 2018
@Kingwl
Copy link
Contributor

Kingwl commented Sep 13, 2018

what is the expected behavior?

@RyanCavanaugh
Copy link
Member

@DanielRosenwasser I have the same question

@kevinder
Copy link

kevinder commented Sep 13, 2018

This is correct. We don't know that res.success isn't undefined or null, which would mean it's possibly not false and therefore not { success: false, error: string }

I'm wondering why the if (res.success === true) { example above should compile then, because couldn't success still be undefined or null in the else block?

@haltman-at
Copy link

Hey, I just wanted to point out that this issue now has additional relevance in TypeScript 3.6 due to the addition of the Generator type and the new IteratorResult type. I was writing some code with generators, expecting I could use while(!result.done) to loop over the case where the generator was not done, but found in fact I had to use while(result.done === false). Might be a reason to revisit this?

@Tanja-4732
Copy link

Tanja-4732 commented Jul 25, 2020

I have experienced similar behaviour, and the following code helped me to work around this limitation:

(Assuming we have foo with the discriminating boolean-property "discBool" and the optional property "optionalProperty" which is only present in one of the two sub-types of foo's super-type. In other words: If discBool is false, optionalProperty is present, if discBool is true, optionalProperty is not present in foo)

switch (foo.discBool) {
  case false:
    doSomething(foo.optionalProperty);
}

while this code didn't work:

if (!foo.discBool) {
  doSomething(foo.optionalProperty); // Error here
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Help Wanted You can do this
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants