When is a variables' Type Never?
Demystifying the Typescript never Type by looking at situations where you may encounter the never Type in your day-to-day activities.
Have you come across the never
type before? If not, let me tell you more about this in this issue of All Things Typescript. In this issue, I want to explore situations in which a variable can have the never
Type.
But before we do that, what’s the never Type? To understand this, let’s explore Types first. Types, in Typescript and in language for that matter represent a series of possible values, for instance, for a variable whose type is a number, the possible values for the variable are any number in the planet, from zero to infinity (or negative infinity).
The same applies to a variable whose type is a string, it could be anything from a single character in the alphabet to a whole blog post or the bible. When it comes to the any
type, this encompasses all values, be it numbers
, strings
, boolean
, arrays
, boolean
, and any
type (pun intended) of value that can exist - which is why I discourage you from using the any
type - we will explore this in a future issue of All Things Typescript.
When it comes to the never
type, it’s the opposite of the any
type, it represents the absence of possible values. It means that a variable, whose type is never, can not exist (its values don’t exist).
So, I know what you are thinking right about now, when is a variable Type never? Let’s have a look at a couple of scenarios:
Control Flow Narrowing
The control flow is the order in which the computer executes statements in a script. - MDN Docs
If you narrow your variable, there is a possibility that you will end up with a situation where the final else is going to result in the never type. Take the following function, which accepts an input of either number or string - assume it does something interesting.
function doSomething(input: number | string) {
// body
}
If we narrowed our input, by exhaustively checking every possible scenario, so that we can do something different based on the variable type, we can do it something like this:
if(typeof input === "string") {
// do something with string
} else if (typeof input === "number") {
// do something with number
} else {
// NEVER
console.log(input);
}
In the first, if condition, we are narrowing to the string
type when the condition is true. And in the second, condition, we are narrowing to the number
type when the condition is true, But in the last condition, since our variable can only be either number
or string
, then it can’t possibly have a value, hence the never type is inferred, as shown below.
The inferred return type of function expressions with unreachable Endpoint
In Javascript, and obviously by extension, in Typescript, we have multiple ways of declaring functions, we have the function declaration, the most common one by far, then the arrow function expression, and then the function expression. In this section, we are talking about the latter two.
When using either a function expression or the more compact arrow syntax to define a function and the function never returns a value, for instance, throws an error or an infinite loop, then typescript will automatically infer the never type.
Let’s see an example:
const doSomething = (message: string) => {
throw new Error(message)
}
In the first example above, our arrow function expression, throws an error, meaning it never returns, and hence, it automatically infers the never type as its return type.
This behavior can be seen when using the less compact function expression, as shown below:
const doSomething = function(message: string) {
throw new Error(message)
}
Explicit Annotation of the never
Type
Another way you can have a never type is either explicitly annotating the variable to never or assigning to a variable another variable that’s never. I don’t have a good reason as to why anyone would want to do that, so if you do, feel free to drop a comment in the section below.
On top variables, functions can be annotated to return the never type. This kind of function must have an unreachable endpoint, for instance, an infinite loop, as shown below.
function doSomething(): never {
while(true) {
// infinite loop
}
}
Another example of such a function throws an error:
function doSomething(): never {
throw Error("Error Message")
}
In either of the case above, if you returned either of the above functions or assigned them to a variable, the inferred type is going to be never.
There are a few more things to understand when it comes to the never type:
For instance, A variable whose type is never can only be assigned a never value, in this case, another variable of type never and nothing else, including the any
type. If you tried, you will get an error similar to the following:
The same applies to functions, whose return type is explicitly annotated as never
, can only return never or have an unreachable endpoint, as previously discussed. If you return some other type, Typescript is going to be screaming at you, as shown below:
Conclusion
In this issue of All Things Typescript, we demystified the Typescript never
Type and looked at situations where you may encounter the Type. In most cases, when you encounter the never type, it signals there could be something wrong with your code that results in Typescript inferring the never type.
That’s it for this week and If you enjoyed reading this article, please like, share, and subscribe to learn Typescript every week.
Thank you ❤️.