Skip to content
Back to skills

Exhaustive Switches

ASecurity

Make switch and match statements provably total so adding a case forces every site to handle it, with no default that hides the gap. Use when branching over an enum, union, or sealed type.

  • 7 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 5, 2026
ai-agentstypescriptrustgokotlinexpressgit

Security analysis

A100/100

Scanned September 5, 2026

npx -y skills add Amey-Thakur/AI-SKILLS --skill exhaustive-switches --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Exhaustive Switches?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Exhaustive Switches
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/amey-thakur-exhaustive-switches/badge)](https://www.skillsdirectory.com/skills/amey-thakur-exhaustive-switches)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: exhaustive-switches
description: Make switch and match statements provably total so adding a case forces every site to handle it, with no default that hides the gap. Use when branching over an enum, union, or sealed type.
---

# Exhaustive switches

A switch over a fixed set of cases is a promise to handle all of them, and a
`default` branch quietly voids that promise: add a new enum member and every
switch keeps compiling while silently doing the wrong thing. The goal is to make
the compiler, not a runtime log, tell you which sites need the new case. That
converts a whole category of "forgot to update" bugs into build errors.

## Method

1. **Delete the catch-all default on closed sets.** When the type is an enum or
   sealed union you own, drop `default:` so the compiler must verify every
   member is handled. A default belongs only to genuinely open input like a raw
   int.
2. **Turn on the exhaustiveness check your language offers.** TypeScript narrows
   a `switch` and lets you assign the leftover to `never`. Rust `match` is
   exhaustive already: do not add `_ =>`. Use `-Wswitch-enum` in Clang or GCC,
   and sealed classes with `when` in Kotlin.
3. **Add a `never`-typed floor.** In TypeScript, end the switch with
   `const _exhaustive: never = value;`. When someone adds a variant, this line
   stops compiling at exactly the switches that failed to handle it.
4. **Model the domain as a closed union.** Prefer a discriminated union or
   sealed hierarchy over loose strings so the type system knows the full
   case list. Exhaustiveness checking has nothing to verify against an open
   `string`.
5. **Return from each branch instead of mutating one shared variable.** A switch
   expression that yields a value forces every arm to produce one; a statement
   that assigns can silently leave a case unassigned. Prefer switch expressions.
6. **When a default is unavoidable, make it loud.** For open input, throw in the
   fallthrough, `throw new Error('unhandled: ' + x)`, rather than returning a
   default value. Failing fast beats returning a plausible wrong answer.

## Checks

- Add a throwaway enum member and compile: do the switches error, or stay green?
- Search `default:` and `_ =>`: does each guard truly open input, not a closed
  enum?
- Does every branch return a value, or can one path fall through assigning
  nothing?

## Boundaries

Open-ended input such as network payloads, user strings, or version-skewed data
from another service legitimately needs a default for the unknown:
exhaustiveness is for sets you fully own. Languages without sealed types or
`never` can only approximate this, so lean on lint rules and tests there.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…