-
Notifications
You must be signed in to change notification settings - Fork 3
Description
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
switchhandling, especially when using TypeScript or flow-based control. - Prevents silent bugs caused by
defaultswallowing unexpected values. - Provides flexibility with the
allowEmptyoption for teams that want explicit placeholders without introducing runtime logic. - Complements existing ESLint rules like
no-fallthroughand TypeScript’s--noImplicitReturns.