Assert Never Utility
The `assert-never` package provides a lightweight helper function designed for TypeScript's exhaustive checks on discriminated unions. Its primary purpose is to ensure that all possible cases of a union type are handled within a conditional block, leveraging the TypeScript compiler to catch unhandled cases at compile time. At runtime, if an unhandled case is encountered, `assertNever` will throw an `Error` by default, indicating a logical flaw. Alternatively, it can be configured to fail silently. The current stable version is 1.4.0. As a focused utility for a core TypeScript pattern, its release cadence is typically infrequent, with updates primarily for compatibility or minor feature enhancements rather than rapid iteration. Its key differentiator is its simplicity and direct application for a common TypeScript development pattern, making union type handling more robust.
Common errors
-
Argument of type '...' is not assignable to parameter of type 'never'.
cause You have not exhaustively handled all cases of the discriminated union before calling `assertNever`. TypeScript correctly identifies that there are still possible types remaining that could be passed to `assertNever`, which expects 'never'.fixAdd additional `if` or `case` statements to handle all remaining types in your discriminated union before the call to `assertNever`. -
Unhandled value in exhaustive check: [object Object]
cause The `assertNever` function was reached at runtime, meaning an unhandled case of the discriminated union was encountered. This indicates a logic error where a type was present that TypeScript did not prevent due to either a type assertion, a dynamic value, or an incomplete exhaustive check.fixReview the logic preceding `assertNever` to ensure all possible runtime values are handled. If the type is correct but needs to be ignored at runtime, consider `assertNever(value, true)`.
Warnings
- gotcha The primary purpose of `assertNever` is to throw a runtime Error if an unhandled case is encountered, signaling an unexpected state. If silent failure or custom logging is desired, pass `true` as the second argument: `assertNever(value, true)`.
- gotcha This library relies on TypeScript's type inference and strictness for compile-time exhaustive checks. For this pattern to function correctly, ensure your `tsconfig.json` has `strict: true` (or at least `strictNullChecks`, `noImplicitReturns`, and `noFallthroughCasesInSwitch`) enabled.
Install
-
npm install assert-never -
yarn add assert-never -
pnpm add assert-never
Imports
- assertNever
const assertNever = require('assert-never').assertNever;import { assertNever } from 'assert-never';
Quickstart
import { assertNever } from "assert-never";
type Shape =
| { type: 'circle'; radius: number }
| { type: 'square'; side: number }
| { type: 'triangle'; base: number; height: number };
function getArea(shape: Shape): number {
switch (shape.type) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.side ** 2;
// If 'triangle' was added to Shape but not handled here, TypeScript would error.
default:
// TypeScript will error here if there are unhandled types in the union.
// At runtime, if an unhandled shape reaches this point, it will throw an Error.
return assertNever(shape);
}
}
// Example usage
const myCircle: Shape = { type: 'circle', radius: 5 };
const mySquare: Shape = { type: 'square', side: 4 };
const myTriangle: Shape = { type: 'triangle', base: 3, height: 6 };
console.log("Area of circle:", getArea(myCircle));
console.log("Area of square:", getArea(mySquare));
console.log("Area of triangle:", getArea(myTriangle));
// To demonstrate the runtime error (uncomment the next lines to run):
// type NewShape = Shape | { type: 'pentagon'; side: number };
// const unhandledShape: NewShape = { type: 'pentagon', side: 5 };
// try {
// // This line would cause a TypeScript error if getArea still expects 'Shape'
// // and 'pentagon' is not handled. For runtime demo, we cast to bypass TS.
// console.log("Area of unhandled shape:", getArea(unhandledShape as Shape));
// } catch (e: any) {
// console.error("Caught expected runtime error:", e.message);
// }