eslint-plugin-guard-clauses
v0.0.1
Published
ESLint plugin to enforce guard clauses and reduce code nesting
Maintainers
Readme
eslint-plugin-guard-clauses
ESLint plugin to enforce guard clauses and reduce code nesting for cleaner, more readable code.
Installation
pnpm add -D eslint-plugin-guard-clausesUsage
ESLint 9+ (Flat Config)
import guardClauses from 'eslint-plugin-guard-clauses';
export default [
{
plugins: {
'guard-clauses': guardClauses
},
rules: {
'guard-clauses/if-else-to-early-return': 'warn'
}
}
];Or use the recommended config:
import guardClauses from 'eslint-plugin-guard-clauses';
export default [
{
plugins: {
'guard-clauses': guardClauses
}
},
guardClauses.configs.recommended
];Or use the strict config (all rules as errors):
import guardClauses from 'eslint-plugin-guard-clauses';
export default [
{
plugins: {
'guard-clauses': guardClauses
}
},
guardClauses.configs.strict
];Configs
recommended: All rules enabled as warningsstrict: All rules enabled as errors
Rules
if-else-to-early-return
Suggests using early return instead of if-else when both branches return a value.
Problem
Functions that consist only of an if-else statement where both branches return can be simplified using early return pattern (guard clauses).
// Bad
function checkAge(age) {
if (age >= 18) {
return 'adult';
} else {
return 'minor';
}
}Solution
Invert the condition and return early, then return the default case:
// Good
function checkAge(age) {
if (age < 18) {
return 'minor';
}
return 'adult';
}Benefits
- Reduces nesting and cognitive complexity
- Makes the code flow more linear and easier to read
- Eliminates unnecessary else blocks
- Follows the guard clause pattern
When it triggers
This rule will flag a function when ALL of these conditions are met:
- The function body contains ONLY an if-else statement (no other statements)
- Both the if and else branches end with a return statement
- Works with function declarations, function expressions, and arrow functions
When it won't trigger
- Functions with multiple statements
- If statements without an else clause
- When only one branch returns
- Arrow functions with expression bodies (e.g.,
x => x ? a : b) - When there are statements before or after the if-else
no-else-return
Disallow else blocks after return statements in if blocks.
// Bad
function foo(x) {
if (x) {
return true;
} else {
doSomething();
}
}
// Good
function foo(x) {
if (x) {
return true;
}
doSomething();
}Rationale: If the if block contains a return statement, the else block is unnecessary because the code will only reach that point if the condition was false.
prefer-guard-clauses
Prefer guard clauses to reduce nesting depth.
// Bad
function processUser(user) {
if (user) {
if (user.isActive) {
if (user.hasPermission) {
return doWork(user);
}
}
}
return null;
}
// Good
function processUser(user) {
if (!user) return null;
if (!user.isActive) return null;
if (!user.hasPermission) return null;
return doWork(user);
}Benefits: Guard clauses reduce cognitive complexity by flattening nested if statements. The main logic becomes more prominent and easier to follow.
if-else-chain-to-early-return
Convert if/else-if/else chains to early returns for better readability.
// Bad
function getStatus(code) {
if (code === 200) {
return 'OK';
} else if (code === 404) {
return 'Not Found';
} else if (code === 500) {
return 'Error';
} else {
return 'Unknown';
}
}
// Good
function getStatus(code) {
if (code === 200) return 'OK';
if (code === 404) return 'Not Found';
if (code === 500) return 'Error';
return 'Unknown';
}Note: This rule only triggers for chains with 2 or more else-if clauses to avoid false positives on simple if-else statements.
no-nested-ternary-return
Discourage nested ternary expressions in return statements.
// Bad
function getLevel(score) {
return score > 90 ? 'A' : score > 80 ? 'B' : score > 70 ? 'C' : 'F';
}
// Good
function getLevel(score) {
if (score > 90) return 'A';
if (score > 80) return 'B';
if (score > 70) return 'C';
return 'F';
}Rationale: Nested ternaries are hard to read and maintain. Using if statements with early returns makes the logic clearer.
reduce-boolean-return
Simplify boolean returns from if-else statements by returning the condition directly.
// Bad
function isValid(x) {
if (x > 10) {
return true;
} else {
return false;
}
}
// Good
function isValid(x) {
return x > 10;
}Rationale: When an if-else statement only returns boolean literals, you can return the condition (or its negation) directly.
no-else-if-after-return
Disallow else if blocks after return statements in if blocks.
// Bad
function classify(x) {
if (x > 10) {
return 'high';
} else if (x > 5) {
return 'medium';
} else {
return 'low';
}
}
// Good
function classify(x) {
if (x > 10) return 'high';
if (x > 5) return 'medium';
return 'low';
}Rationale: When an if block returns, else if is redundant since the code won't continue. Using independent if statements reduces nesting and makes the code more linear.
prefer-early-continue
Prefer early continue in loops to reduce nesting.
// Bad
for (const item of items) {
if (item.isValid) {
processItem(item);
doMore();
}
}
// Good
for (const item of items) {
if (!item.isValid) continue;
processItem(item);
doMore();
}Benefits: Early continue statements reduce nesting in loops, making the main logic easier to follow. This is the loop equivalent of early return.
no-lonely-if
Disallow if statements as the only statement in else blocks.
// Bad
if (x > 10) {
console.log('high');
} else {
if (x > 5) {
console.log('medium');
}
}
// Good
if (x > 10) {
console.log('high');
} else if (x > 5) {
console.log('medium');
}Rationale: A lonely if in an else block adds unnecessary nesting. Using else if is clearer and more idiomatic.
prefer-guard-at-function-start
Prefer guard clauses at the start of functions.
// Bad
function processUser(user) {
doSetup();
if (!user) return null; // Guard should be first
return user.data;
}
// Good
function processUser(user) {
if (!user) return null; // Guard at top
doSetup();
return user.data;
}Benefits: Guard clauses at the start validate inputs early, following the "fail fast" principle. This prevents unnecessary work and makes preconditions explicit.
Development
Setup
pnpm installTesting
pnpm test # Run tests once
pnpm test:watch # Run tests in watch modeLinting
pnpm lint # Check for linting errors
pnpm lint:fix # Fix linting errorsRelease
pnpm releaseLicense
ISC
