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
Boolean() cannot be used to perform a null check #16655
// CompilesconstnullCheckOne=(value?: number)=>{if(!!value){returnvalue.toFixed(0);}return'';}constnullCheckTwo=(value?: number)=>{if(Boolean(value)){// Object is possibly 'undefined'returnvalue.toFixed(0);}return'';}
Expected behavior:
Both examples compile.
Actual behavior:
The latter example fails w/ Object is possibly 'undefined'.
Explanation
To my knowledge !!value and Boolean(value) are equivalent. I'm wondering what is the reason behind not supporting the second case. One reason I can think of would be an imported, non-typescript module, globally overriding it to something like: Boolean = (value) => !value.
I'm wondering what is the reason behind not supporting the second case
It's not supported because no one has done the work to support it yet.
One reason I can think of would be an imported, non-typescript module, globally overriding it to something
In general we don't account for this. Literally anything in the global scope could be mutated to be something different and it'd be insane to say Well you can't use substr because maybe someone messed with it.
I think we could replace the signature with
declare function Boolean<T>(x: T | false | 0 | "" | null | undefined): x is T;
which is almost correct (it fails to do the right then in the else case when x is 0 coming from a non-literal numeric type).
I've literally never seen someone use Boolean like this, though. Is there any difference between if (value) and if (Boolean(value)) ?
Reacted by Jakub Korzeniowski, Kirill Groshkov, Kim Mu-hun, Tyler C Laprade, CFA and Maikel van Dort
I don't think there's any, apart from implicit vs. explicit type cast. I personally use it, because it makes type conversion to primitives consistent (Boolean(1), String(1), Number('1'), etc.)
One common pattern using the boolean constructor is .filter(Boolean) to remove falsy values from an array (including null and undefined).
Reacted by Aluan Haddad, Esa-Matti Suuronen, John Wright, Alexandre Galays, Toni Villena, Misha Kaletsky, Thom, Alexis Tyler, ExE Boss, Miguel Carvajal and 73 more
@Kovensky - that is a good point. filter as it currently stands however, can't be used to implement the type guards either. Both of the examples below infer to (number | undefined)[]. Would also be really cool to address it.
This was reverted due to it causing a breaking change in 3.5. The simple repro looks like this:
declareconstBullean: {new(value?: any): Boolean;<T>(value?: T): value is Exclude<T,false|null|undefined|''|0>;};declareconstarr: any[];// Was any[], now (3.5) unknown[]constarr2=arr.filter(Bullean);
There might be something wrong in generic inference, but it's not yet clear. We'll need this scenario to work before adding it back in.
Reacted by Georgii Dolzhykov, Bartosz Gościński, ExE Boss, Jed, Gustavo, Peter Solopov, Kirill Groshkov, Konstantin Bulgakov, muradsofi, Artur Santiago and 3 more
This was reverted due to it causing a breaking change in 3.5. The simple repro looks like this:
There might be something wrong in generic inference, but it's not yet clear. We'll need this scenario to work before adding it back in.
Just for interest, what is the reason that Exclude narrows any to unknown?
constarr=[1,2,undefined];constfiltered: Array<number>=arr.filter(elem=>elem!=undefined);constfiltered2: Array<number>=arr.filter(Boolean);// Type '(number | undefined)[]' is not assignable to type 'number[]'.// Type 'number | undefined' is not assignable to type 'number'.// Type 'undefined' is not assignable to type 'number'.// Type '(number | undefined)[]' is not assignable to type 'number[]'.
Both these scenarios right now still break. I found a temporary workaround with
constworkaround=arr.filter((elem): elem is number=>elem!==undefined);
However it would be nice not to need the type guard. Is there any hope for that :)?
constvalues: Array<number|undefined>=[1,2,undefined,4,undefined,6]functionnonNullish<V>(v:V): v is NonNullable<V>{returnv!==undefined&&v!==null}values.filter(nonNullish).map(v=>v.toFixed(5))
Reacted by ExE Boss, Sirui Li, Devin Schulz, Ken Huang, Saurabh Charde and Neri Yarden
Reacted by nediamond, Jack Asher, Ken Huang and Shinji Nakamatsu
Another pattern is using Boolean() along with && - a common pattern for example in embedded JS expressions in JSX with React:
functionCartMessage(props: {cartItems: number}){return<div>Cart contains ${props.cartItems} items</div>;}exportdefaultfunctionApp(){constcartItems=0asnumber|null;return(<divclassName="App"><h3>Using number on LHS of &&</h3><h4>(see 0 below)</h4><div>{cartItems&&<CartMessagecartItems={cartItems}/>}</div><h3>Using Boolean() on LHS of &&</h3><h4>(no 0 below)</h4><div>{Boolean(cartItems)&&<CartMessagecartItems={cartItems}/>}</div></div>);}
In the first example, the 0 is rendered on the screen because it is falsey:
To avoid this, the Boolean() constructor is used to convert the number on the LHS of the && to a boolean, but this causes a problem with the types:
Type 'number | null' is not assignable to type 'number'.
Type 'null' is not assignable to type 'number'.ts(2322)
If the Boolean() constructor behaved the same way that !! (double negation) did, this wouldn't be an issue.
Also affects strings:
letname=''asstring|null;// Renders empty string, usually not a problem but can cause crashes with React Native// https://github.com/facebook/react-native/issues/20764{name&&<Profilename={name}/>}// 💥 Type 'string | null' is not assignable to type 'string'.{Boolean(name)&&<Profilename={name}/>}
But sometimes the Boolean() option may be desirable because of its expressiveness.
Reacted by Isaac Clements, gilmeir-arnac, muradsofi, Jaroslav Polakovič, Charles Kornoelje, Brooke Hart, Steve Frost, Brody McKee, Otavio de Aguiar, Michael Dovzhenko and 9 more
Another workaround that I haven't seen mentioned is to create another Boolean object (Tested with TS 5.1.6) :
typeFalsy=false|0|""|null|undefinedconst_Boolean=<Textendsany>(v: T): v is Exclude<typeofv,Falsy>=>Boolean(v)constfiltered=[1,null,undefined,2,3].filter(_Boolean)// number[]constorders=settled.map((result)=>(result.status==="fulfilled" ? result.value : false)).filter(_Boolean)// array of fullfilled values
It would be great if that was the default behaviour of Boolean
This was reverted due to it causing a breaking change in 3.5. The simple repro looks like this:
declareconstBullean: {new(value?: any): Boolean;<T>(value?: T): value is Exclude<T,false|null|undefined|''|0>;};declareconstarr: any[];// Was any[], now (3.5) unknown[]constarr2=arr.filter(Bullean);
There might be something wrong in generic inference, but it's not yet clear. We'll need this scenario to work before adding it back in.
I'm doing my yearly rabbit-hole dive of why-can't-we-have-.filter(Boolean) - and this comment seems to be the only one that this solution doesn't solve for - but based on it already existing in v5.4.5, maybe it should be considered unrelated, and no longer a blocker to updating lib/es5.d.ts along the lines of mattpocock/ts-reset#168 (comment)?
interfaceArray<T>{// this overload already exists in lib/es5.d.ts, re-adding here to make sure the overloads are checked in the correct orderfilter<SextendsT>(predicate: (value: T,index: number,array: T[])=>value is S,thisArg?: any): S[];// this is the proposed change to lib/es5.d.tsfilter<Pextends(value: T,index: number,array: T[])=>unknown>(predicate: P,thisArg?: unknown,): (PextendsBooleanConstructor ? Exclude<T,null|undefined|0|false|''> : T)[];}
What do you think? Can we have .filter(Boolean) work mostly as you'd expect in 2024? The only objection I can find that still seems to be a problem is your Bullean example, and since that's already in a released version of TypeScript, why not opportunistically get in a fix for .filter(Boolean) after all these years??
Reacted by 王杰, Tyler C Laprade, CFA, Justin H., Mikey Stengel, Baptiste Marchand, Sung Jeon, Michael McKenzie, Cameron Steffen, Paweł Dymara, Bernard and 2 more
Reacted by Liam O'Boyle, Phillip Huang, Michael McKenzie, Will Stamper, peelz, Jordan Harband, Jake Leventhal, Nazar Kotsyuba, Spencer Miller, Paweł Dymara and 5 more
This seems particularly strange with the new predicate inference in 5.5. If you do .filter(t => !!t) you get narrowing, but if you do .filter(Boolean), you don't:
Reacted by Liam O'Boyle, Kirill Groshkov, Jordan Harband, Reece Dunham, inker, Sung Jeon, Zihua Li, Justin H., Mira Šerý, Tyler C Laprade, CFA and 26 more
Activity
RyanCavanaugh commentedon Jun 20, 2017
It's not supported because no one has done the work to support it yet.
In general we don't account for this. Literally anything in the global scope could be mutated to be something different and it'd be insane to say Well you can't use
substrbecause maybe someone messed with it.I think we could replace the signature with
which is almost correct (it fails to do the right then in the
elsecase whenxis0coming from a non-literal numeric type).I've literally never seen someone use
Booleanlike this, though. Is there any difference betweenif (value)andif (Boolean(value))?kujon commentedon Jun 20, 2017
I don't think there's any, apart from implicit vs. explicit type cast. I personally use it, because it makes type conversion to primitives consistent (
Boolean(1),String(1),Number('1'), etc.)Jessidhia commentedon Jun 22, 2017
One common pattern using the boolean constructor is
.filter(Boolean)to remove falsy values from an array (includingnullandundefined).kujon commentedon Jun 22, 2017
@Kovensky - that is a good point.
filteras it currently stands however, can't be used to implement the type guards either. Both of the examples below infer to(number | undefined)[]. Would also be really cool to address it.ForbesLindesay commentedon Mar 18, 2019
Jakub Korzeniowski (@kujon) You can make
.filterwork with the custom function by adding explicit types:It's a little bit verbose, but very flexible.
RyanCavanaugh commentedon May 22, 2019
This was reverted due to it causing a breaking change in 3.5. The simple repro looks like this:
There might be something wrong in generic inference, but it's not yet clear. We'll need this scenario to work before adding it back in.
ForbesLindesay commentedon May 23, 2019
I'm not sure what more I can do here?
danielrentz commentedon May 27, 2019
Just for interest, what is the reason that
Excludenarrowsanytounknown?lgraziani2712 commentedon Nov 28, 2019
Here is a playground repro BTW https://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=7&pc=2#code/GYVwdgxgLglg9mABAJwKZRMsBRAHgQ2gAoBKALkQG9EYATCgZymRjAHNEBfAbQF0qAUImGIICJonzJkiALyJu1OhQDkARhVcANFRr1EKgEybOOpfvUneAbgFCRaDFknT7IkQDoAtvgAORVAAbVC85AD5EIJCPOjlZeSNNAH5EMBBAwMQKKK8SN3dED2AYQKhUZADg0NkInMQAQnk0jJJbTiA
AllainPL commentedon Jul 17, 2020
Both these scenarios right now still break. I found a temporary workaround with
However it would be nice not to need the type guard. Is there any hope for that :)?
LorenzHenk commentedon Sep 4, 2020
Another workaround (playground link):
7 remaining items
karlhorky commentedon Oct 3, 2022
Another pattern is using
Boolean()along with&&- a common pattern for example in embedded JS expressions in JSX with React:https://codesandbox.io/s/falling-https-zvg2xv?file=/src/App.tsx
In the first example, the
0is rendered on the screen because it is falsey:To avoid this, the
Boolean()constructor is used to convert the number on the LHS of the&&to a boolean, but this causes a problem with the types:If the
Boolean()constructor behaved the same way that!!(double negation) did, this wouldn't be an issue.Also affects strings:
Alternatives
There are alternatives to doing this:
But sometimes the
Boolean()option may be desirable because of its expressiveness.kkmuffme commentedon Jun 18, 2023
I think the "Awaiting more feedback" label can be removed, as tons of people seem to have this issue.
Hebilicious commentedon Aug 13, 2023
Another workaround that I haven't seen mentioned is to create another Boolean object (Tested with TS 5.1.6) :
It would be great if that was the default behaviour of Boolean
resolve microsoft#16655; Boolean() can be type-guard for null checks
Add truthy type guard
Add truthy type guard
Add truthy type guard
SergeOlabin commentedon Dec 20, 2023
did anyone found a way to make it work within a project so that Boolean() can act as a null check ?
upd:
as has been posted adding following seems solving MOST of the cases.
although, in my case next piece still errored byts:2345:
at the same time with no error
JH1ller commentedon Apr 2, 2024
Serge Olabin (@SergeOlabin) sadly that custom BooleanConstructor doesn't work when you do
Boolean(valueA && valueB)Would really be great this get this fixed from TS side.
mmkal commentedon May 30, 2024
Ryan Cavanaugh (@RyanCavanaugh) maybe this is known, but some time between when you wrote this in 2019 and today, this breaking change seems to have happened anyway. Playground link
I'm doing my yearly rabbit-hole dive of why-can't-we-have-
.filter(Boolean)- and this comment seems to be the only one that this solution doesn't solve for - but based on it already existing in v5.4.5, maybe it should be considered unrelated, and no longer a blocker to updatinglib/es5.d.tsalong the lines of mattpocock/ts-reset#168 (comment)?Here's another playground with that proposed fix applied as a module augmentation, along with some type tests for the edge cases/prior bugs reported over the years from this issue, #50387, mattpocock/ts-reset#168, and #31551. I put links to GitHub issue comments as inline comments in the code. Here's the augmentation to
Array<T>required (same would be needed forReadonlyArray<T>):What do you think? Can we have
.filter(Boolean)work mostly as you'd expect in 2024? The only objection I can find that still seems to be a problem is yourBulleanexample, and since that's already in a released version of TypeScript, why not opportunistically get in a fix for.filter(Boolean)after all these years??CodingDive commentedon Jun 8, 2024
The boolean constructor is so useful for maps, JSX and in general far more readable than doing the double negation.
Ryan Cavanaugh (@RyanCavanaugh) Daniel Rosenwasser (@DanielRosenwasser) Sheetal Nandi (@sheetalkamat) could you please consider fixing this in one of the next versions? 🙏
epmatsw commentedon Jun 21, 2024
This seems particularly strange with the new predicate inference in 5.5. If you do
.filter(t => !!t)you get narrowing, but if you do.filter(Boolean), you don't:https://www.typescriptlang.org/play/?ts=5.5.2#code/MYewdgzgLgBFAWBLMBzCMC8MDaBXMAJgKYBmyRBANDCQIYA2ER1UATrszAAzUCMAujFrpQkKAG4AUKOhx2CAJ69McJKggA6MvShFWACn1QAlJgB8MAISWTUmbDa5FAJhUJkaLYh179AIRAQeiJaMGMpGBggA
chore(refactor): replace Boolean with !!
chore: mostly replace Boolean with !!
chore: mostly replace Boolean with !!