You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Strange boolean-discriminant narrowing with strictNullChecks off #10564
typeResult={success: true}|{success: false,error: string}functionhandleError(res: Result){if(res.success===true){return;}res.error;// OK}functionhandleError2(res: Result){if(res.success!==false){return;}res.error;// OK}functionhandleError3(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.
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 }
Ryan Cavanaugh (@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.
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?
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?
Reacted by Brian Kim, Владимир and Philipp Rudloff
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)
Activity
RyanCavanaugh commentedon Aug 26, 2016
This is correct. We don't know that
res.successisn'tundefinedornull, which would mean it's possibly notfalseand therefore not{ success: false, error: string }gcnew commentedon Aug 26, 2016
Ryan Cavanaugh (@RyanCavanaugh) While you are technically right (and I was thinking the same), when
strictNullChecksis offnullandundefinedare not modelled at all and such differences don't surface in other situations. For examplehandleErrorhas 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.Kingwl commentedon Sep 13, 2018
what is the expected behavior?
RyanCavanaugh commentedon Sep 13, 2018
Daniel Rosenwasser (@DanielRosenwasser) I have the same question
kevinder commentedon Sep 13, 2018
I'm wondering why the
if (res.success === true) {example above should compile then, because couldn'tsuccessstill be undefined or null in theelseblock?haltman-at commentedon Sep 3, 2019
Hey, I just wanted to point out that this issue now has additional relevance in TypeScript 3.6 due to the addition of the
Generatortype and the newIteratorResulttype. I was writing some code with generators, expecting I could usewhile(!result.done)to loop over the case where the generator was not done, but found in fact I had to usewhile(result.done === false). Might be a reason to revisit this?Tanja-4732 commentedon Jul 25, 2020
I have experienced similar behaviour, and the following code helped me to work around this limitation:
(Assuming we have
foowith the discriminating boolean-property "discBool" and the optional property "optionalProperty" which is only present in one of the two sub-types offoo's super-type. In other words: IfdiscBoolis false,optionalPropertyis present, ifdiscBoolis true,optionalPropertyis not present infoo)while this code didn't work:
chore(tsconfig): enable strictNullChecks and noImplicitAny
RyanCavanaugh commentedon Aug 18, 2026
This now works as expected