Transcript from the "The Case for Double Equals" Lesson
>> Kyle Simpson: So let me bring this down to a case that I wanna make, and this is much more of a direct case than I've never made before about this topic. Most of the time prior to this point, my essential takeaway or my essential message to people was, == and === can coexist in your code, and you can make choices, you don't have to avoid the ==, I'm going to make a stronger argument to this point.
[00:00:32] The stronger argument that you should prefer == in all possible places. And I know this gonna shock a few people but you're gonna have to have an open mind to listen to this argument. Taking everything in whole in totality that we've talked about together, including the fact that it is my claim that code that understands types is better code regardless of the choice.
[00:00:57] Within that context, I think I can make the case that == is preferable to ===.
>> Kyle Simpson: So knowing the types is always better than not knowing them. The uncertainty of code is what make code hard to read, it makes code susceptible to bugs. Some people respond to that problem of not knowing their types by saying, I need to change my coding style to use static typing, like TypeScript and the others.
[00:01:28] We're gonna talk about TypeScript in just a moment but I don't think that that is the only response to this problem. There's another response to this problem,
>> Kyle Simpson: So == is not about comparisons with unknown type, that's not what it's for and that's not what you should use it for, that is the opposite of what most people claim.
[00:01:51] Most people claim that they use == when they don't know the types, and then they fall into these bugs and I'm saying don't use it in that scenario at all. Never use the == when you don't know the types, ever. Only use the == when you know the types.
[00:02:09] And as I said, you should strive to know the types in your comparisons as much as possible. It's about comparisons with known types and optionally, if you like to have coercions. Maybe all of your comparisons you know the types and maybe all of those types are always equal and you never use coercion, and still in that scenario == is preferable.
>> Kyle Simpson: But maybe some of the times you wanna take advantage of coercion, and then == is essentially your only option.
>> Kyle Simpson: So let's dig into the nuance here because I know some of you are not gonna believe me just right off the bat, okay? Let's dig into the nuance here, if you know the types in a comparison, and I think you should.
[00:02:55] If you know the types in a comparison and both of the types are the same, then we already know that == is identical to ===. We already know their interchangeable, in the case where you know them and they are the same. So you're saying, yeah, but that doesn't mean that I don't know how to use it.
[00:03:15] My claim is, using === is unnecessary in that scenario and you should prefer the shorter ==. That is not just about using one fewer character, okay? Some of you know that TypeScript does a lot of work, even if you don’t do any types at all. Here I have no types annotated once so ever and it still does a bunch of inference, type inference, okay?
[00:03:41] In this particular case what the error is telling me is I should not use a === because the types are different, and this will always be false. Now I don't know about you, but I find that to be a particularly useful error message. It is telling me don't do something stupid that would always fail, 100%, guaranteed to fail, all right?
[00:04:06] It knows that it'll fail, and it tells me don't do it unnecessarily. My claim is that if we find to be reasonable, then the counter positive is also reasonable. If it is pointless to use it when the types don't match, it is unnecessary to use it when the types do match.
>> Kyle Simpson: If we like for our linter to complain when it'd be pointless, then we ought to have a linter that complains when it's unnecessary.
>> Kyle Simpson: So if you know the types in a comparison, and they're different, this is where things get interesting, because supposedly using the === is protecting you and saving you.
[00:04:52] But if you know the types and you know them to be different, a single === will always fail, which is the whole reason for that TypeScript rule we just talked about.
>> Kyle Simpson: It would definitely be broken, you're with me? So your only two options in that case, if you know the types and they don't match you, have one of two options, you can either not do the comparison or you can use ==.
[00:05:20] Those are your only two options and if you know the types in a comparison, and they're different, the only equivalent to a single == would be two or more ===.
>> Kyle Simpson: You can not have a === and a == be equivalent when the types are different, that is impossible.
[00:06:01] And it gets even worse if you have to do three or four depending upon the complexity of the coercion. Now I say slower, in air quotes because we're talking about microseconds. But it is absolutely not possible for a === to be faster in this scenario. In the best possible scenario, it's equal and actually, in reality, it's just a tiny bit slower to list them out multiple times.
[00:06:30] Now I'm not making the case that on performance alone that's an argument, because the microsecond difference is not that relevant. But this is part of the larger case that === is not somehow superior to ==. Many people have said === is faster cuz it doesn't coerce. That's a apples to oranges comparison, cuz you're saying one === is faster than one == and they're not equivalent.
>> Kyle Simpson: If you actually looked at an apples to apples two === is always going to be a little bit slower than one ==.
>> Kyle Simpson: So if you're in this scenario where you could do two === or three or four, or you could do one ==, I say you should prefer the faster of the two which is the single ==, part of the argument, certainly not the entirety of the argument.
[00:07:24] And finally, if the types are different, and two or more are what the comparisons, that's what you would use, is two or more of the ===, my claim is that it's distracting the reader with unnecessary type information, like in the null undefined case, for example. And the no one find case, you're distracting the reader there is these two different empty values and they sort of have weird quirky differences.
[00:07:48] That's not necessary for the reader to see in most cases, in most cases you're distracting them by listing explicitly and what you have to use is the more properly abstracted coercion that it's cleaner with the ==. Now remember, this is if you know the types, right, I'm only making these claims if you know the types.
[00:08:08] And by know the types, I mean that it is obvious to the reader of the code what the types will be.
>> Kyle Simpson: So what about, sorry, there's one more, the summary slide, of course. This is if you know the types, so in summary, whether the types match or not, == is the more sensible choice when you know the types, okay?
[00:08:31] When you know the types, == is the more sensible choice, so let's look at the other side, in those cases where you don't know the types. If you do not know the types, that means you don't fully understand the code, that may be an unnecessary requirement. It is not possible to guarantee 100%, you always know the types in the entire system, that's not reasonable and rational.
[00:08:56] So I recognize that there will be times when comparisons happen, and there's some uncertainty. My claim is that that should be a rarity rather than the status quo. But what it does mean, whether it's a rarity or the status quo, is that that part of the code is harder to understand as a result of that uncertainty.
[00:09:17] So not knowing the types means that you don't fully understand that code. The best response is to refractor it so that you can know the types, if possible. If you don't know the types, and that uncertainty of knowing just has to be dealt, then at least make sure that that is obvious to the reader.
[00:09:42] Make sure that it is obvious to the reader that there is some uncertainty here. Put a code comment, structure it in such a way that the reader doesn't have a false sense of security, they think they know what's happening, and actually it's uncertain. It should be super obvious that it's uncertain in whatever possible way you can, because again we're trying to communicate our intent.
[00:10:03] My intent here is to tell you that it's uncertain, just so you know, beware there'll be dragons, right, that's what we wanna do with our code. So it should be obvious to the reader, and the most obvious signal you can use about that uncertainty is ===. If you followed what I argued in the first set of clauses, which is that the == is preferable when you know the types, then the usage of === should be reserved for the cases where you don't know the types.
>> Kyle Simpson: So that it signals to the reader, I don't know the types, I'm trying to protect myself.
>> Kyle Simpson: It should jump out, the reader should see, there's a === here. That means there are some uncertainty of the types, and we need to restrict ourselves so we don't run into a corner case.
>> Kyle Simpson: Also, I would say that not knowing the types is equivalent to assuming that type conversion or type coercion will occur. It's equivalent to assuming that because if you don't know the types, the worst case scenario is that they will not match and they will not match in some weird way that invokes some weird corner case of coercion.
[00:11:17] You need to assume the worst case. So not knowing the types of equivalent to that worst case coercion. And the only sensible response to that, the only safe response to that is protect yourself from that with a ===. In summary if you can't or won't, use a style where you can know and have obvious types, the only thing that is sensible and reasonable is for you to use the ===.
[00:11:47] This isn't meant to be an attack against you if use ===. I'm just trying to reframe this discussion in a way that actually moves the discussion forward rather than a sort of religious debate of well, this one book said never use it, and I've never even considered anything else.
[00:12:03] I'm actually trying to make a rational set of analysis here to come to some real conclusion, not just the thing that one guy said, okay? And you can test my analysis, if you don't agree with my rationale, that's okay. But I'm laying out this case so that you can test the rationale.
[00:12:20] And I would say this, even in that scenario, where every place that you would use a === would be the same, it would be equivalent to a ==, and so you say, you know, we just want the simpler thing we want to use === everywhere because that's what Lyndhurst says.
[00:12:38] What you are actually doing is sending the wrong semantic signal to a reader of your code. Which is I’m protecting myself in all of these cases because I don't know about the types.
>> Kyle Simpson: And that is not communicating what you put to communicate to future readers of the code.
[00:12:53] Future readers of the code that see the semantic signal, I don't know about the types, so I've to fall back to some === to protect myself, those future readers of the code are much more likely to rewrite the code, because they don't understand it. They're gonna do what you should have done the first time.
[00:13:11] They're gonna refactor it to where the types are known. Uncertainty is not a good thing for your code basis.
>> Kyle Simpson: So in summary, making your types known and obvious just leads to better code. There's no argument that can be made to the contrary. Knowing the types leads to better code and if the types are known, == is always best.
>> Kyle Simpson: In every scenario == is best when the types are known and in any scenario where you can't you should fall back to ===. I'm fully aware that case is quite controversial. And it's unlikely that any of you are convinced at the moment by that discussion. But what I would really like to ask you is to go back and think about it.
[00:14:00] Go back and test this rationale and test it against your code. Ask yourself if you really are writing better code that is more clearly communicating its intent by simply using === everywhere. I can't make those decisions for you but I think I've made the most solid case that I can, for why they should be re-analysed.