When working with javascript I always assumed that the double NOT operator (!!) behaves the same way as Boolean(), when trying to get a true / false out of a statement; however, since working with TypeScript I recently came across a scenario where they behaved differently, and I am trying to understand why that is. Take the following code for example:
import React from 'react'
interface Props {
myNumber: number | null
}
const Test: React.FC<Props> = ({ myNumber }) => {
return (
<>
{/* Example 1 */}
{!!myNumber && <span>{myNumber.toString()}</span>}
{/* Example 2 */}
{Boolean(myNumber) && <span>{myNumber.toString()}</span>}
</>
)
}
export default Test
Under example #1 the code works as I expect it: If the is rendered that means that myNumber passed the !! check, so it can never be null, thus myNumber.toString() will not return an error.
Under example #2 however, myNumber.toString() will return an error stating that "Object is possibly null", even though this code should only be reached if the Boolean(myNumber) returns true, and Boolean(null) should always be false.
Am I wrong thinking they should behave exactly the same? Is there something related to the TypeScrtipt compiler that I am missing? Thanks!
Boolean is typed as a function of type BooleanConstructor.
BooleanConstructor can be further broken down as <T>(value?: T | undefined) => boolean.
TypeScript does not consider the inner workings of a function when checking to see if a value may be null | undefined.
// Here TypeScript can directly infer that myNumber is indeed truthy
// by the preceeding double bang (!!) check.
const numberStrWithDoubleBang = !!myNumber && myNumber.toString();
// Here TypeScript cannot directly infer that myNumber is indeed truthy.
// To Typescript the function is a black box that takes in a number and
// returns a boolean, it does not know how the boolean value is calculated.
const numberStrWithFunction = isTruthy(myNumber) && myNumber.toString();
// While the isTruthy function above could easily have an implementation
// that does a simple truthy check on the passed in value as follows:
const isTruthy = <T>(value?: T | undefined) => !!value;
// It could also easily have an implementation with an output with no direct
// connection to the truthy status of the passed in value such as:
const isTruthy = <T>(value?: T | undefined) => {
const correctedValue = value ?? 0;
const midPoint = correctedValue / 2;
if (Math.random() * correctedValue > midPoint) {
return true;
}
return false;
};
At this point in time inferrence of null | undefined state via a function is considered to complex or the performance impact would be to great.
NOTE: !!0 and Boolean(0) are both false as 0 is a falsy value, if you need to get the string representation of zero you will need to update your checks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With