Skip to content

Commit f3ac3ef

Browse files
committed
types-grammar, ch4: adding 'boolean gotcha' section
1 parent ebd507f commit f3ac3ef

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

types-grammar/ch4.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,135 @@ But another minor fact you might consider: in performance benchmarks I've run ma
10751075
10761076
I'd observe that even many diehard `===` fans tend to concede that `== null` is at least one such case where `==` is preferable.
10771077

1078+
#### `==` Boolean Gotcha
1079+
1080+
Aside from some coercive corner cases we'll address in the next section, probably the biggest gotcha to be aware of with `==` has to do with booleans.
1081+
1082+
Pay very close attention here, as it's one of the biggest reasons people get bitten by, and then come to despise, `==`. If you take my simple advice (at the end of this section), you'll never be a victim!
1083+
1084+
Consider the following snippet, and let's assume for a minute that `isLoggedIn` is *not* holding a `boolean` value (`true` or `false`):
1085+
1086+
```js
1087+
if (isLoggedIn) {
1088+
// ..
1089+
}
1090+
1091+
// vs:
1092+
1093+
if (isLoggedIn == true) {
1094+
// ..
1095+
}
1096+
```
1097+
1098+
We've already covered the first `if` statement form. We know `if` expects a `boolean`, so in this case `isLoggedIn` will be coerced to a `boolean` using the lookup table in the `ToBoolean()` abstract operation. Pretty straightforward to predict, right?
1099+
1100+
But take a look at the `isLoggedIn == true` expression. Do you think it's going to behave the same way?
1101+
1102+
If your instinct was *yes*, you've just fallen into a tricky little trap. Recall early in this chapter when I warned that the rules of `ToBoolean()` coercion only apply if the JS operation is actually activating that algorithm. Here, it seems like JS must be doing so, because `== true` seems so clearly a "boolean related" type of comparison.
1103+
1104+
But nope. Go re-read the `IsLooselyEqual()` algorithm (for `==`) earlier in the chapter. Go on, I'll wait. If you don't like my summary, go read the specification algorithm[^LooseEquality] itself.
1105+
1106+
OK, do you see anything in there that mentions invoking `ToBoolean()` under any circumstance?
1107+
1108+
Nope!
1109+
1110+
Remember: when the types of the two `==` operands are not the same, it prefers to coerce them both to numbers.
1111+
1112+
What might be in `isLoggedIn`, if it's not a `boolean`? Well, it could be a string value like `"yes"`, for example. In that form, `if ("yes") { .. }` would clearly pass the conditional check and execute the block.
1113+
1114+
But what's going to happen with the `==` form of the `if` conditional? It's going to act like this:
1115+
1116+
```js
1117+
// (1)
1118+
"yes" == true
1119+
1120+
// (2)
1121+
"yes" == 1
1122+
1123+
// (3)
1124+
NaN == 1
1125+
1126+
// (4)
1127+
NaN === 1 // false
1128+
```
1129+
1130+
So in other words, if `isLoggedIn` holds a value like `"yes"`, the `if (isLoggedIn) { .. }` block will pass the conditional check, but the `if (isLoggedIn == true)` check will not. Ugh!
1131+
1132+
What if `isLoggedIn` held the string `"true"`?
1133+
1134+
```js
1135+
// (1)
1136+
"true" == true
1137+
1138+
// (2)
1139+
"true" == 1
1140+
1141+
// (3)
1142+
NaN == 1
1143+
1144+
// (4)
1145+
NaN === 1 // false
1146+
```
1147+
1148+
Facepalm.
1149+
1150+
Here's a pop quiz: what value would `isLoggedIn` need to hold for both forms of the `if` statement conditional to pass?
1151+
1152+
...
1153+
1154+
...
1155+
1156+
...
1157+
1158+
...
1159+
1160+
What if `isLoggedIn` was holding the number `1`? `1` is truthy, so the `if (isLoggedIn)` form passes. And the other `==` form that involves coercion:
1161+
1162+
```js
1163+
// (1)
1164+
1 == true
1165+
1166+
// (2)
1167+
1 == 1
1168+
1169+
// (3)
1170+
1 === 1 // true
1171+
```
1172+
1173+
But if `isLoggedIn` was instead holding the string `"1"`? Again, `"1"` is truthy, but what about the `==` coercion?
1174+
1175+
```js
1176+
// (1)
1177+
"1" == true
1178+
1179+
// (2)
1180+
"1" == 1
1181+
1182+
// (3)
1183+
1 == 1
1184+
1185+
// (4)
1186+
1 === 1 // true
1187+
```
1188+
1189+
OK, so `1` and `"1"` are two values that `isLoggedIn` can hold that are safe to coerce along with `true` in a `==` equality check. But basically almost no other values are safe for `isLoggedIn` to hold.
1190+
1191+
We have a similar gotcha if the check is `== false`. What values are safe in such a comparison? `""` and `0` work. But:
1192+
1193+
```js
1194+
if ([] == false) {
1195+
// this will run!
1196+
}
1197+
```
1198+
1199+
`[]` is a truthy value, but it's also coercively equal to `false`?! Ouch.
1200+
1201+
What are we to make of these gotchas with `== true` and `== false` checks? I have a plain and simple answer.
1202+
1203+
Never, ever, under any circumstances, perform a `==` check if either side of the comparison is a `true` or `false` value. It looks like it's going to behave as a nice `ToBoolean()` coercion, but it slyly won't, and will instead be ensnared in a variety of coercion corner cases (addressed in the next section). And avoid the `===` forms, too.
1204+
1205+
When you're dealing with booleans, stick to the implicitly coercive forms that are genuinely activating `ToBoolean()`, such as `if (isLoggedIn)`, and stay away from the `==` / `===` forms.
1206+
10781207
## Coercion Corner Cases
10791208
10801209
I've been clear in expressing my pro-coercion opinion thus far. And it *is* just an opinion, though it's based on interpreting facts gleaned from studying the language specification and observable JS behaviors.

0 commit comments

Comments
 (0)