;

TypeScript never Type


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.

Introduction to TypeScript Never Types

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.

What is the never Type in TypeScript?

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.
  • The never type is used to indicate that this function won’t produce a result.

Common Use Cases for never

The never type is often used in the following scenarios:

  1. Error-Throwing Functions: Functions that terminate by throwing an error, rather than completing with a return value.
  2. Infinite Loops: Functions designed to run indefinitely, such as background services or event listeners that don’t terminate.
  3. Exhaustive Type Checks: When working with unions or switch statements, never helps enforce exhaustive checks, ensuring that all possible cases are handled.

Declaring Functions with never Return Type

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.

Example: Error-Throwing Function

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.

Example: Infinite Loop Function

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.
  • TypeScript enforces that this function doesn’t have an endpoint or a return value, ensuring that it’s used with caution.

Real-World Examples of Using never Type

Example 1: Exhaustive Type Checks in Switch Statements

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.
  • The 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.

Example 2: Handling Unexpected Cases in Union Types

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.
  • The never type ensures all Status cases are covered, so if a new status is added, TypeScript will require you to update the function accordingly.

Difference Between 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.

Example Comparison of void and never

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.

Key Takeaways

  1. Purpose of never: The never type is for functions that don’t complete normally, like those that throw errors or loop indefinitely.
  2. Exhaustive Checks with never: Using never in switch statements ensures all possible cases are handled, making code safer and more predictable.
  3. Difference from void: void is used for functions that don’t return a value, whereas never is for functions that never successfully exit.
  4. Best Practices: Use never for error-handling functions, infinite loops, and exhaustive type checks to ensure code robustness.
  5. Type-Safe Union Handling: never ensures that your union types are fully checked, helping you catch unexpected cases in complex data structures.

Summary

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.