Understanding the ‘Never’ Type in TypeScript

Updated: January 7, 2024 By: Guest Contributor Post a comment

Grasping the ‘Never’ type in TypeScript is crucial for developers seeking robust type-checking. This guide demystifies its usage with practical examples.

Introduction to the ‘Never’ Type

Coming to grips with the various data types offered by TypeScript is key to utilizing the full power of the language. One type, however, often slips under the radar: the never type. This special TypeScript type is used to represent values that never occur. At first, it may seem abstract or unnecessary, but understanding never can improve your code’s type safety and clarify your intent.

TypeScript is a statically typed superset of JavaScript that compiles down to plain JavaScript. Its robust type system includes both simple and complex types, with never falling into the latter category. The never type is the type of values that never occur, such as a function that doesn’t return or a variable under a type guard that can’t happen.

Basic Example of ‘Never’ Type

function error(message: string): never {
  throw new Error(message);
}

function fail() {
  return error('Something failed');
}

In this snippet, the error function is annotated with the never type because it’s expected to throw an error and never return a value. When the fail function calls error, TypeScript understands that this path also never successfully returns, hence no type other than never is appropriate.

Using ‘Never’ for Exhaustiveness Checking

interface Square {
  kind: 'square';
  size: number;
}

interface Circle {
  kind: 'circle';
  radius: number;
}

type Shape = Square | Circle;

function getArea(shape: Shape) {
  switch (shape.kind) {
    case 'square':
      return shape.size * shape.size;
    case 'circle':
      return Math.PI * shape.radius ** 2;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

In this code, the never type is used to ensure that all members of the Shape union type are handled in the getArea function. If a new member is added to the union, TypeScript will throw an error because the _exhaustiveCheck will no longer be of type never, thus preventing potential runtime errors due to unhandled cases.

Advanced Usage of ‘Never’ with Unreachable Code

function exhaustiveCheck(x: never): void {
  throw new Error('Unexpected object: ' + x);
}

function updateShape(shape: Shape) {
  // Imagine that 'shape' can only be a 'square' or 'circle'
  if (shape.kind !== 'square' && shape.kind !== 'circle') {
    exhaustiveCheck(shape);
  }
  // rest of the update logic
}

In this advanced example, the exhaustiveCheck function is designed to handle the cases that should be impossible. In this case, if the shape argument is neither a square nor a circle, it must be of type never. If the function receives a value, it implies a serious flaw in our logic or system, and the issue should be flagged during development time.

Conclusion

The never type is an essential element in TypeScript’s type system that ensures better type safety and clarifies developer intentions. Employing never can drive home the integrity of your code and is a testament to the sophistication available to TypeScript developers. By understanding and utilizing never, you can catch errors at compile time, making your code more reliable and maintainable.