shouldNeverHappen: A Simple Trick for Catching the Impossible in TypeScript

shouldNeverHappen: A Simple Trick for Catching the Impossible in TypeScript
Photo by Martin Wyall / Unsplash

In every codebase, there are situations that should just never happen. Maybe it's a value that should have been validated earlier, or a branch in your logic that you’re 100% sure will never be executed. And yet… bugs happen.

Instead of scattering throw new Error(...) statements throughout your code, it’s better to encapsulate that logic with a dedicated utility function: shouldNeverHappen.

Let’s talk about why this pattern is useful, how to use it properly, and a few important considerations you might not have thought of.


💡 What is shouldNeverHappen?

It’s a small helper function that you can call when you hit a panic-level error—a defect that indicates your assumptions are broken.

Here’s a basic version:

export const shouldNeverHappen = (msg?: string, ...args: any[]): never => {
  console.error(msg, ...args);
  if (isDevEnv()) {
    debugger;
  }
  throw new Error(`This should never happen: ${msg}`);
};

This is designed to fail loudly, immediately, and in developer-friendly ways:

  • Logs to the console
  • Breaks into the debugger when in development mode
  • Throws an error to stop execution

✅ Why You Should Use It

1. Improves Readability

Using shouldNeverHappen('Missing user') is way more expressive than a raw throw statement. Anyone reading the code knows this is an unexpected failure.

2. Centralized Panic Handling

If you ever want to add logging, analytics, or alerting, you only need to do it in one place. This is great for production observability (think: Sentry, Datadog, etc.).

3. Enforces TypeScript’s never

Returning never helps TypeScript understand that this function does not return, allowing better control flow analysis and eliminating the need for ! non-null assertions.

4. Developer Experience Boost

During development, opening the debugger immediately saves you from guessing where things went wrong. It's like a smart breakpoint.


🧪 Example Usage

const user = maybeUser ?? shouldNeverHappen('User expected but missing');

You expect maybeUser to exist, and if it doesn’t, your assumption is broken—fail fast.

if (!config.allowUpload) {
  return shouldNeverHappen('Uploads should always be allowed here');
}

This pattern helps ensure your app never silently continues in a bad state.


🧠 Extra Considerations

✅ Only Use for Defects

Don’t use shouldNeverHappen for user input errors, API failures, or things that are expected to go wrong occasionally. This is for true internal logic failures, not recoverable runtime issues.

📦 Add Logging/Telemetry Hooks

Consider extending the utility to report to tools like Sentry:

import * as Sentry from '@sentry/node';

export const shouldNeverHappen = (msg?: string, ...args: any[]): never => {
  console.error(msg, ...args);
  Sentry.captureException(new Error(msg));
  if (isDevEnv()) debugger;
  throw new Error(`This should never happen: ${msg}`);
};

🌐 Works Great with Feature Flags

If you're doing gradual rollouts or A/B testing, shouldNeverHappen can act as a guardrail for assumptions that should never be broken after rollout.

🚨 Avoid in Library Code

This pattern is great for internal applications, but in published libraries, you should generally use standard error types and let consumers decide how to handle them.


🧵 Bonus: Add Context to the Error

You can pass any number of arguments to provide more context:

shouldNeverHappen('Unexpected null in payment flow', { transactionId, user });

This makes debugging way easier later.


🧼 Clean, Simple, Powerful

The real strength of shouldNeverHappen is that it keeps your codebase clean, consistent, and intentional. It separates real bugs from expected failures, and it does so in a way that improves both the developer experience and the runtime safety of your application.

If you’re not using something like this yet—you should start today.

Support Us