The never
type in TypeScript represents a type of value that never occurs, making it useful for scenarios where a function never successfully completes or handles exhaustive cases that shouldn’t logically occur. Understanding how to use never can make your code more robust and your intentions clearer. In this tutorial, we’ll explore what never is, how to use it, and when it’s most beneficial.
TypeScript’s never
type represents a value that never occurs, which is especially useful for functions that throw errors or run infinite loops, as they don’t successfully complete. never also serves a valuable role in TypeScript’s type-checking, ensuring exhaustive checks in code like switch
statements. In this tutorial, we’ll cover how to use never
effectively, making your code safer and easier to understand.
The never
type in TypeScript signifies that a function or expression doesn’t produce a value because it never completes. This could happen because the function throws an error, enters an infinite loop, or fails to reach any reachable endpoint.
function throwError(message: string): never {
throw new Error(message);
}
In this example:
throwError
is a function that never returns a value, as it always throws an error.never
type is used to indicate that this function won’t produce a result.The never
type is often used in the following scenarios:
never
helps enforce exhaustive checks, ensuring that all possible cases are handled.Functions with a never
return type don’t successfully return to the calling code. Here’s how never
can be used in functions that always throw errors or loop indefinitely.
A function with a never
return type is perfect for handling scenarios where an error is thrown, as it won’t have a successful outcome.
function throwError(message: string): never {
throw new Error(message);
}
throwError("An unexpected error occurred!"); // The function exits with an error, never returning
In this example:
throwError
takes a message parameter, but it never returns a value because it always throws an error.You might use a function that loops indefinitely for tasks like background polling or event listening.
function runForever(): never {
while (true) {
console.log("Running...");
}
}
Here:
runForever
will never complete because it’s an infinite loop.In a switch
statement, using never
can help ensure all cases of a union type are handled, making code more resilient to future changes.
type Shape = "circle" | "square" | "triangle";
function getShapeArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI * Math.pow(10, 2); // Assuming a radius of 10
case "square":
return Math.pow(10, 2); // Assuming side length of 10
case "triangle":
return 0.5 * 10 * 5; // Assuming base of 10 and height of 5
default:
// Using `never` ensures exhaustive checks
const exhaustiveCheck: never = shape;
throw new Error(`Unhandled shape: ${exhaustiveCheck}`);
}
}
In this example:
getShapeArea
calculates areas for three shapes: circle
, square
, and triangle
.never
type in default enforces that all possible cases are handled. If a new shape is added to Shape
, TypeScript will show an error unless it’s handled in the switch
statement.When working with union
types, never
can ensure that your code covers all cases.
type Status = "loading" | "success" | "error";
function handleStatus(status: Status): string {
if (status === "loading") {
return "Loading...";
} else if (status === "success") {
return "Operation successful!";
} else if (status === "error") {
return "An error occurred.";
}
// Ensure all cases are handled
const exhaustiveCheck: never = status;
throw new Error(`Unexpected status: ${exhaustiveCheck}`);
}
In this case:
handleStatus
returns a message based on the status.never
type ensures all Status
cases are covered, so if a new status is added, TypeScript will require you to update the function accordingly.never
and void
While both void
and never
imply functions that don’t return meaningful values, they serve different purposes.
void
: Used when a function does not return a value but completes its execution.never
: Used when a function does not complete execution or produce a value, such as in cases of errors or infinite loops.function logMessage(message: string): void {
console.log(message); // The function completes, so `void` is appropriate
}
function throwError(message: string): never {
throw new Error(message); // The function never completes successfully, so `never` is used
}
In this example:
logMessage
completes normally, so void
is the correct type.throwError
throws an error, meaning it doesn’t reach a normal endpoint, so never
is used.never
type is for functions that don’t complete normally, like those that throw errors or loop indefinitely.never
in switch
statements ensures all possible cases are handled, making code safer and more predictable.void
is used for functions that don’t return a value, whereas never
is for functions that never successfully exit.never
for error-handling functions, infinite loops, and exhaustive type checks to ensure code robustness.never
ensures that your union types are fully checked, helping you catch unexpected cases in complex data structures.The never
type in TypeScript is a powerful tool for representing functions and scenarios that don’t complete or produce a value. Whether you’re handling errors, running infinite loops, or ensuring exhaustive type checks, never
adds a level of safety and clarity to your code. By understanding how and when to use never, you can make your TypeScript applications more robust and maintainable, reducing the chance of unexpected behavior.