Check out a free preview of the full TypeScript Fundamentals, v3 course:
The "Bottom Types: never" Lesson is part of the full, TypeScript Fundamentals, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Mike discusses bottom types which describe types that hold no possible value in the system with the key word never and walks through a use case for a bottom type in an exhaustive conditional. How to handle an exhaustive conditional with an unreachable error using an error subclass is also covered in this segment.

Get Unlimited Access Now

Transcript from the "Bottom Types: never" Lesson

>> All right, let's talk about the opposite of top types, a bottom type. Bottom types describe things that can hold no possible value. So you can think of it almost like you're free to choose anything within an empty set, that this variable will be happy to hold, right?

[00:00:25] It's like anything you want, and you may pick it from this perfectly empty box, [LAUGH] that is never. Sounds like a really abstract concept, and if you're like me, if this is the first time you've heard about never, you're wondering what is the point of this thing? Is it here just because somebody loves math, and because they feel like they want some great symmetry in the universe?

[00:00:50] Or is there something that I can actually do with this that is useful? And I'm pleased to tell you, it's the second one, there is something very useful you can do with it, and that thing is exhaustive conditionals. So let's look at this situation. We've got a class called car, a class called truck, and then a type called vehicle, which can be either car or truck.

[00:01:18] And let's say that we wanna do something with our vehicles. And we just have a vehicle, we don't know whether it's car or truck, in fact, behind the scenes I have something called obtain random vehicle, but just for now, say, it's one of the two. So, what we can do is say, okay, we got the type car here, we're gonna handle the truck case.

[00:01:41] Great, it's a truck we can call the tow method. If it's a vehicle, sorry, if the vehicle is a car, we can call drive, and that's something that's only available on the car, okay, like class. Apparently trucks can't drive, apparently, I don't have a truck. So, great, what's left?

[00:02:02] What's left after we've eliminated the possibility that it's car, and we've eliminated the possibility that it's a truck? What else could this thing be? It's kind of a trick question, the answer is nothing. There's nothing else that could be, nothing, all that's left is never, there's nothing else that could be.

[00:02:34] We've taken care of 100% of the cases of things we know about, right? Like cars and trucks, and there's nothing else this could be. So the fact that we're saying this ends up as never, that's another way of saying we've handled all of the cases. We have an exhaustive conditional that handles every possibility of this vehicle being one of the things, right?

[00:03:04] So, let's see what happens if someone comes along and adds boat. So what they've done is they've created a class boat, and they've added boat as one of the things of vehicle could now be, could now be a car, or a truck, or a boat. And I haven't touched the rest of the code.

[00:03:28] Look what happens here. We see, wait a minute, this vehicle, when it gets into this last else clause, there's something left over, it could be a boat, right? This isn't a never, there's not nothing left over after we handle car and truck, there's boat leftover, and we're alerted to this now.

[00:03:52] So, imagine a world where this stuff here, the car, the truck, the boat, the vehicle type, this might be defined in some other file, maybe in a project with 10,000 JavaScript files. Someone alters this, and now all of the conditions, all of the different places and the rest of the code base, where they're looking through, they wanna make sure they handle every case that this thing could possibly be.

[00:04:18] Now you see all of these nice errors light up, where you know you have to go in, and handle the boat case now. Either deliberately handle it or deliberately not handle it, but attention is needed, right? These are places if they have something down here that says, basically, like up here my vehicle had better be a never, right?

[00:04:43] Or saying, I'm gonna create a variable of type never, and the only thing that can fit in never is never, it's the most specific, finicky type there is in TypeScript. It's only happy with other nevers. So, this is only gonna work if there's nothing left, and down here, there's boat left, and so this conditional is no longer exhaustive.

[00:05:12] The way I like to handle this is I create, and I have the same code, if you look for this code like my GitHub account, you probably see a couple dozen projects pop up. I should really just put it in the library so I can use it that way.

[00:05:28] But I use this unreachable error all the time, and it takes as an argument and never, and it has a message as its second argument. So, the purpose of taking a never as the first argument is, I wanna be able to pass something into it that will have a compile error unless it's a never, right?

[00:05:51] So that's about creating a compile error at the right spot. The message is so that if for some reason, maybe have been lying in my types like cast something, something was an any, and it's like weekend my types in some way. If I ever reached this line and I ever actually throw this error in code that I didn't think I could ever reach.

[00:06:14] I get a very clear indication that something was really weird here, and my assumptions about being unable to reach this line of code, had been broken. And I have to take a close look at what actually is going on here. And the way I would do this, is I would say, instead of up here where we create a variable and we set my vehicle equal to something, down here in this last else block here, I would throw the error, right?

[00:06:44] And in this case, I get a compile error because boat is leftover, and I'm alerted to it. So by kind of wrapping this in an error subclass, we know that one of three things will happen, depending on whether things break and how they break. So, first, we've written our types, well, everything is handled before we even reach that else clause, we never entered the else, right?

[00:07:16] And everything's happy, so that's great. Another thing that could happen, we catch this code change at compile time, which is the example we're seeing in this error message here, right? Someone added boat, this piece of my code needs an update, I'm alerted to it, and I can give it some attention, that could happen, that's what we're seeing here.

[00:07:41] Finally, if somehow something slips through, I at least get a good error message and I at least know that I thought I could never reach this thing. And I get a description here, I mean, maybe be more descriptive than this, but I could say, I didn't even think this was a possible vehicle.

[00:08:05] Maybe you log out what this thing is, so that it becomes more actionable for you and you'd go back to this code and you'd say, where did boat come from? My types don't suggest that boats possible, how did we end up with a boat? And you would track that down.

[00:08:20] So, it's also important to remember this works really well with switch statements. And the only difference is you would throw your unreachable error in your default case clause, right? That's it, whatever it is, it's sort of the fall through. If any of your specific cases with type guards aren't met, this is the thing you thought could never happen, but if it does happen, you get a nice error message.

[00:08:46] Exhaustive conditionals, this is cool stuff and if some programming languages support a more formalized version of this, like rust, I believe, it demands that every conditional you create is exhaustive, right? You have to write in your no ops explicitly. So this gives you the ability if you choose to use this never concept in this way, you can do the same thing for your code.