Boolean in JavaScript and TypeScript
boolean is a fun primitive data type in JavaScript. In TypeScript, it allows for a total of four values
Wait, four?
Boolean in JavaScript #
boolean can take the values of true and false. Values from other types can be truthy or falsy,
like undefined or null.
let b = true
if(b) console.log('logged')
b = false
if(b) console.log('not logged')
b = undefined
if(b) console.log('not logged')
b = null
if(b) console.log('not logged')
Values other than undefined, null or false considered falsy are "" (empty string), -0 and 0, as well as NaN.
To get the boolean value of any value, you can use the Boolean function:
Boolean(false) // false
Boolean(true) // true
Boolean("false") // true βοΈ
Boolean("Hey folks") // true
Boolean({}) // true
Boolean([]) // true
Boolean(123.4) // true
Boolean(Symbol()) // true
Boolean(function() {}) // true
Boolean(undefined) // false
Boolean(null) // false
Boolean(NaN) // false
Boolean(0) // false
Boolean("") // false
Rule of thumb: All empty values evaluate to false. Empty object {} and
empty array [] (which is an object itself) do have value as they are containers
for other values.
The Boolean function is really good to filter empty values from collections:
const collection = [
{ name: 'Stefan Baumgartner', age: 37 },
undefined,
{ name: 'D.', age: 36 },
false
{ name: 'C.', age: 2},
false
]
collection.filter(Boolean) // handy!
Together with Number β which converts all values into their number counterpart or NaN, this
is a really cool way of getting to actual values quickly:
const x = ["1.23", 2137123, "wut", false, "lol", undefined, null]
.map(Number)
.filter(Boolean) // [1.23, 2137123] π
Boolean exists as a constructor and has the same conversion rules as the Boolean function.
However, with new Boolean(...) you create a wrapping object, making value comparisions truthy,
but reference comparisions falsy:
const value = Boolean("Stefan") // true
const reference = new Boolean("Stefan") // [Boolean: true]
value == reference // true
value === reference // false
You get to the value via .valueOf():
value === reference.valueOf() // true
I have a REPL for you to check.
The use of Boolean as a function is obviously great, but new Boolean has very limited use.
If you know a practical use case, please let me know.
Boolean in TypeScript #
boolean in TypeScript is a primitive type. Be sure to use the lower case version and donβt
refer to object instances from Boolean
const boolLiteral: boolean = false // π
const boolObject: Boolean = false // π
It works, but itβs bad practice as we really rarely need new Boolean objects.
You can assign true, false and undefined and null to boolean in TypeScript
without strict null checks.
const boolTrue: boolean = true // π
const boolFalse: boolean = false // π
const boolUndefined: boolean = undefined // π
const boolNull: boolean = null // π
With that, boolean is the only one we can express fully through union types:
type MyBoolean = true | false | null | undefined // same as boolean
const mybool: MyBoolean = true
const yourbool: boolean = false
When we enable the strictNullChecks compiler flag, the set of values reduces to true and false.
const boolTrue: boolean = true // π
const boolFalse: boolean = false // π
const boolUndefined: boolean = undefined // π₯
const boolNull: boolean = null // π₯
So our set reduces to two values in total.
type MyStrictBoolean = true | false
We can also get rid of null values with the NonNullable helper type:
type NonNullable<T> = T extends null | undefined
? never
: T;
type MyStrictBoolean = NonNullable<MyBoolean> // true | false
The fact that boolean consists of a limited set of values only used in conditions,
allows for interesting conditional types.
Think of an mutation in a datastore through a function. You set a flag in a function that updates e.g. the user id. You have to provide the user ID then:
type CheckUserId<Properties, AddUserId> =
AddUserId extends true
? Properties & { userId: string }
: Properties & { userId?: string }
Depending on the value of our generic AddUserId, we expect the property userId to be set or to be
optional.
We can make this type more explicit by extending our generics from the types we expect
- type CheckUserId<Properties, AddUserId> =
+ type CheckuserId<
+ Properties extends {},
+ AddUserId extends boolean
+ >
AddUserId extends true
? Properties & { userId: string }
: Properties & { userId?: string }
In use, it might declare a function like this:
declare function mutate<P, A extends boolean = false>
(props: CheckUserId<P, A>, addUserId?: A): void
Note that I even set a default value for A to make sure CheckUserId gives the correct
info depending on addUserId to be set or not.
The function in action:
mutate({}) // π
mutate({ data: 'Hello folks' }) // π
mutate({ name: 'Stefan' }, false) // π
mutate({ name: 'Stefan' }, true) // π₯ userId is missing
mutate({ name: 'Stefan', userId: 'asdf' }, true) // π userId is here
Handy if your code relies a lot on truthy and falsy values. As always, thereβs playground for you.