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 voidWhile 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.