TypeScript never Data Type
Overview
Like every other programming language, JavaScript has its data types and structures. To use JavaScript to write programs, we must be familiar with a handful of its data types. Complex data structures may be created by combining several bits of data. With dynamically typed languages, it might be difficult to tell what type a variable has without logging it, and we risk assigning data to the variable that we don't want. These problems are solved by TypeScript by allowing us to set fixed types for variables so that we are certain of the types.
Introduction
A loosely typed, or dynamically typed, language is JavaScript. This implies that a variable that has been defined with a certain type might change without the need to explicitly change the data's type.
A new basic type named never was added in TypeScript 2.0. It reflects the kinds of qualities that never happen. In the following two locations, the never type is used in Functions that never return have this return type or As the type of variables that are never true under type guards.
When we are convinced that a given event will never occur, we use the Typescript never Data Type. For example, if you write a function that never returns or always throws an exception, you may use the never type on it. Never is a new TypeScript type that represents values that will never be encountered.
What is Never Type?
To properly comprehend never type and its applications, we must first comprehend what a type is and the function it performs in a type system.
A type is a set of values that may be assigned to it. String type, for example, represents an unlimited set of potential strings. When we annotate a variable with the typed string, it can only contain values from that set, i.e. strings:
There is never an empty collection of values in TypeScript. The identical type in Flow, another popular JavaScript type system, is called exactly empty.
Because there are no values in the set, the Typescript never types can have any value, including values of any type. That is why never is also known as an uninhabitable type or a bottom type.
The TypeScript Handbook describes it as the lowest type. I discovered that never placing in the type hierarchy tree, a conceptual model I use to comprehend subtyping, made more sense.
The next proper question is, why do we need Typescript never type?
Why Do We Need Never Type?
We need a type to signify impossibility in our type system, just as we have zero in our counting system to denote the quantity of nothing.
The term impossibility itself is ambiguous. 'Impossibility' presents itself in several ways in TypeScript, including:
-
An empty type with no values may be used to represent the following:
- Parameters in generics and functions that are not allowed.
- The intersection of incompatible kinds.
- An unfulfilled union (a union type of nothingness).
-
When a function finishes executing, it never (pun intended) gives control to the caller, e.g., process. exit from Node
- Not to be confused with the void, which indicates that a function does not return anything valuable to the caller.
-
Another branch that should never (pun intended... well, enough puns for now) be used in a condition type.
-
A rejected request's fulfilled value type
Type Inference in Never Returning Functions
TypeScript sort of implies void by default for function definitions, as illustrated below:
Of course, you can correct it with an explicit annotation:
The main reason for this is backward compatibility with real-world JavaScript code:
How Does Never Work with Unions and Intersections
Typescript Never type has particular features when used in union and intersection types, similar to how the number zero works in addition and multiplication:
-
Union types are never discarded, just as zero added to a number yields the same value. For example, type sample = never | string / string
-
Similar to how zero multiplied by a value yields zero, intersection types never overrule other types. e.g. type Res = never & string // never
These two never type behaviors/characteristics establish the groundwork for some essential use cases we will encounter later on.
How to Use Never Type?
While you're unlikely to use never frequently, there are a few applications for it:
Annotate Inadmissible Function Arguments to Impose Constraints
Given that we can never give a value to the never type, we can use it to put constraints on functions for a variety of use situations.
Ensure that the Switch and if-else statements are Exhaustively Matched
If a function can only accept one never-type parameter, it can never be invoked with any non-never value (without the TypeScript compiler screaming at us):
Such a function may be used to provide exhaustive matching within the switch and if-else statements: by making it the default case, we assure that all instances are handled because what remains must be of a type never. A type error occurs if we omit a possible match by mistake. As an example:
Partially Disallow Structural Typing
Assume we have a method that accepts an argument of type VariantA or VariantB. However, the user must not supply a type that has all properties from both types, i.e., a subtype of both types.
For the parameter, we may use a union type VariantA | VariantB. However, because TypeScript type compatibility is based on structural subtyping, sending an object type with more properties than the parameter's type to a function is permitted (unless you pass object literals):
In TypeScript, the following code line does not produce a type error.
We may partially disable structural typing by never using and preventing users from providing object values that have both properties:
Prevent Unauthorized API Access
Assume we wish to build a Cache instance to read and write data to/from it:
Now, for whatever reason, we want to establish a read-only cache that only allows data to be read via the get function. We may set the put method's parameter to never to prevent it from accepting any value:
Aside from the never type, this may not be an acceptable use case for derived classes. Because I am not an expert in object-oriented programming, please use your discretion.
Denote Conditional Branching That is Potentially Unreachable.
When using infer to generate a type variable within a conditional type, we must provide an else branch for each infer keyword:
Separate Union Members from Union Kinds.
Apart from identifying impossible branches, it should never be used to filter out undesired kinds in conditional types.
As previously stated, when utilized as a union member, the type is never automatically deleted. In other words, in a union type, the never type is meaningless.
When constructing a utility type to choose union members from a union type based on particular criteria, the uselessness of the never type in union types makes it the ideal type to be placed in other branches.
Assume we want or need a utility type Extract to extract union members whose name field is the literal text Sahil and filter out those that do not match:
Remove Keys from Mapped Types
Types in TypeScript are immutable. To remove a property from an object type, we must first construct a new one by modifying and filtering the old one. Those keys are filtered out when we conditionally re-map keys in mapped types to never.
An example of a Castout type that filters out object type attribute depending on their value types is shown below.
Control Flow Analysis Uses Narrow Types
When we never type as the return result of a function, we signify that the function never gives control to the caller once it completes its execution. We may use this to aid control flow analysis in narrowing down kinds.
A function can never return for a variety of reasons, including throwing an exception on all code paths, looping indefinitely, or exiting the application (e.g. process. exit in Node).
We use a method that returns never type in the following code snippet to remove undefined from the union type Sample:
Alternatively, use analysis after the || or ?? operator:
Indicates Crossings of Incompatible Types That are Impossible
This one may feel more like a TypeScript language behavior/characteristic than a practical use for never. Nonetheless, it is critical for deciphering some of the cryptic error messages you may encounter.
By intersecting incompatible types, you may obtain the never type.
And by crossing any kind with never, you obtain the never type.
Reading of Never Type
You may have received error messages with an unexpected never type from Code that was not explicitly annotated with never. This is often due to the TypeScript compiler intersecting the types. It performs this implicitly for you to maintain type safety and soundness.
Here's an example for a better understanding of the concept:
Depending on the type of argument, the function returns a number, a string, or a boolean. To obtain the matching return type, we utilize an indexes access Return_InputType[T].
However, there is a type error for every return statement, namely: Type X is not assignable to type 'never,' where X is a text, numeric, or boolean, depending on the branch.
This is where TypeScript tries to assist us in narrowing the possibilities of problematic states in our program: each return value should be assignable to the type Return_InputType[T], where Return_InputType[T] at runtime might be either a number, a string, or a boolean.
Only by ensuring that the return type is assignable to every conceivable Return_InputType[T], i.e., the intersection of integer, string, and boolean, can we ensure type safety. And where do these three sorts intersect? It's never because they're incompatible with each other. That is why we never see error messages.
You must utilize type assertions (or function overloads) to get around this:
return Math.floor(Math.random() * 2) as Return_InputType[T] return Math.floor(Math.random() * 2) as never
Perhaps another, more obvious example:
Depending on the value of keys at runtime, objet[keys] might be either a string or a number. To be secure, TypeScript adds this constraint, which states that any values we put to objet[keys] must be consistent with both types, string, and number. As a result, it overlaps both types and provides us with the never type.
How to Verify for "Never"?
Checking to see if a type is never is more difficult than it should be.
Consider the following line of Code:
Is the sample correct or incorrect? It may surprise you to learn that the answer is neither: sample is never. In reality,
- TypeScript automatically distributes union types in conditional types.
- There is never an empty union.
- As a result, when distribution occurs, there is nothing over which to distribute, and the conditional type resolves to never again.
Difference between Never and Void
There are many differences between never and void, Some of them are:
Type | Difference 1 | Difference 2 | Difference 3 |
---|---|---|---|
Void | In imperative languages, the void can be conceived of as a single-valued type. These languages do not provide a way to create or consume this value. However, a void function can be viewed as returning it. | You can easily look or notice for Viod in a program code. | The value of the void type might be undefined or null. |
Never | Never, on the other hand, is a type with no values, which implies that a function with this return type can never return normally. This may be accomplished by either generating an exception or failing to finish. | Another way to look at it is that a never value cannot be noticed in a properly written program. In addition to functions that never return (or always throw exceptions), the never type appears when a union type has exhausted all of its available constituents | The never type seems to have no value. |
The Never-Returning Functions
An example of a function that never returns is given here:
The function expression's body is made up of an infinite loop without any break or return statements. Since console.log doesn't throw, there is no way to get out of the loop. As a result, the return type of the function is implied to be never.
Similarly to that, it is assumed that the return type of the following function is never:
Because the function lacks a reachable end point and a return type annotation, as established by control flow analysis, TypeScript infers the never type.
Impossible Type Variables
Another situation in which the never type is inferred is when type guards are never true. In the following example, we see if the value argument may be both a string and an impossible number:
This was obviously a fabricated example, so let's look at a more practical use case. The following example shows how TypeScript's control flow analysis limits union types of variables under type guards. Once we've determined that value is a string, the type checker knows it can't be a number, and vice versa:
Within the last else branch, the value cannot be a string or a number. TypeScript infers the never type in that situation because we've annotated the value argument to be of type string | number, implying that no other type other than string or number is conceivable for the value parameter.
Once control flow analysis has ruled out string and number as choices for a value type, the type checker infers the never type as the sole option left. However, because the value is of the type never, we can't do anything meaningful with it. Hence our editor tools don't display any autocomplete suggestions:
Conclusion
- We first talked about the idea and objectives of never type.
- Never type is a special type, that is the exact opposite of any type
- The never type seems to have no value means we cannot assign any value to the never type.
- The never type describes the return type of a function that always throws an error or has an endless loop.
- Some of its many use cases include limiting functions by utilizing the fact that never is an empty type, and analysis by highlighting erroneous or inaccessible conditional branching.
- The never type can be assigned to any type; however, no type can be assigned to never.
- Conditional types enable the creation of utility types that eliminate potential values from a type using generics and the never type. This method is widely used in Typescript's common utility types.