Skip to content

Add rule to disallow default case in switch statements #32

@tupe12334

Description

@tupe12334

Currently, eslint-plugin-switch-case provides rules for consistent spacing, alignment, and handling of switch cases.
However, there is no rule to disallow the presence of a default case.

For certain codebases, especially when exhaustive checking is handled explicitly (e.g., with TypeScript never checks or pattern matching libraries), having a default case can hide missing branches and lead to unintended behavior.

Use case example

type Status = 'pending' | 'active' | 'inactive';

function handleStatus(status: Status) {
  switch (status) {
    case 'pending':
      return 'wait';
    case 'active':
      return 'go';
    case 'inactive':
      return 'stop';
    default: // ❌ This should not be allowed
      return 'unknown';
  }
}

Instead, we want ESLint to enforce explicit case handling without default, allowing TypeScript to catch unhandled cases:

type Status = 'pending' | 'active' | 'inactive';

function handleStatus(status: Status) {
  switch (status) {
    case 'pending':
      return 'wait';
    case 'active':
      return 'go';
    case 'inactive':
      return 'stop';
    // ❌ Missing cases will cause a compile-time error if no default is present
  }
}

Proposed Rule Name

no-default-case

Options

Default behavior: completely disallow default in switch.

Optional configuration:

{
  "rules": {
    "switch-case/no-default-case": ["error", { "allowEmpty": false }]
  }
}

Detailed Explanation of allowEmpty

The allowEmpty option controls whether an empty default case is permitted in a switch statement.
This is useful when developers want to explicitly signal intent to not handle unexpected cases but still fail loudly at runtime or leave space for future handling, while preventing silent fallbacks.

Motivation

Sometimes, teams want to ban meaningful default case logic but still allow a default branch that is intentionally empty or only contains comments, for example:

switch (value) {
  case 'foo':
    doSomething();
    break;
  case 'bar':
    doSomethingElse();
    break;
  default: // allowed if allowEmpty: true
    // Intentionally left blank
}

Here, the default acts as a placeholder, documenting that other cases may be added later, but without silently swallowing unexpected inputs.

When allowEmpty is false, even this empty default would not be allowed.


Behavior Table

allowEmpty Setting Empty Default Allowed? Default With Code Allowed?
false (default) ❌ No ❌ No
true ✅ Yes ❌ No

Example Configurations

Strict Mode (recommended for exhaustive checks):

{
  "rules": {
    "switch-case/no-default-case": ["error", { "allowEmpty": false }]
  }
}

This enforces that no default is allowed at all.
Ideal for TypeScript exhaustive checking where never is used to detect missing cases.


Allow Empty Default:

{
  "rules": {
    "switch-case/no-default-case": ["error", { "allowEmpty": true }]
  }
}

This allows a default case only if it's completely empty or has only comments, but still prevents unintended logic in default.

Allowed Example:

switch (status) {
  case 'active':
    start();
    break;
  case 'inactive':
    stop();
    break;
  default: // ✅ Allowed
    // No-op, intentionally blank
}

Disallowed Example:

switch (status) {
  case 'active':
    start();
    break;
  case 'inactive':
    stop();
    break;
  default: // ❌ Not allowed
    logError(status); // Has executable code
}

Why this rule should exist

  • Encourages exhaustive switch handling, especially when using TypeScript or flow-based control.
  • Prevents silent bugs caused by default swallowing unexpected values.
  • Provides flexibility with the allowEmpty option for teams that want explicit placeholders without introducing runtime logic.
  • Complements existing ESLint rules like no-fallthrough and TypeScript’s --noImplicitReturns.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions