ESLint InterlaceESLint Interlace
Plugin: maintainabilityRules

no-nested-ternary

ESLint rule documentation for no-nested-ternary

📡 Live from GitHub — This documentation is fetched directly from no-nested-ternary.md and cached for 6 hours.

Keywords: ternary, conditional, nested, readability, ESLint rule, code quality, refactoring, LLM-optimized

Prevent nested ternary expressions for better readability

Prevent nested ternary expressions for better readability. This rule is part of eslint-plugin-maintainability and provides LLM-optimized error messages with suggestions.

Quick Summary

AspectDetails
SeverityWarning (code quality)
Auto-Fix💡 Suggests fixes
CategoryQuality
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForAll projects prioritizing readability and maintainability

Rule Details

Why This Matters

IssueImpactSolution
📖 ReadabilityHard to follow logicUse if-else or extract
🐛 Bug RiskEasy to make mistakesSimpler constructs
🔄 MaintainabilityDifficult to modifyBreak into steps
👀 Code ReviewHarder to reviewClearer structure

Configuration

OptionTypeDefaultDescription
allowstring[][]Contexts where nested ternaries allowed

Examples

❌ Incorrect

// Nested ternary in consequent
const result = condition1 ? (condition2 ? value1 : value2) : value3;

// Nested ternary in alternate
const status = isActive ? 'active' : isPending ? 'pending' : 'inactive';

// Multiple levels of nesting
const color =
  size === 'large'
    ? 'red'
    : size === 'medium'
      ? 'blue'
      : size === 'small'
        ? 'green'
        : 'gray';

✅ Correct

// Single ternary
const result = condition ? value1 : value2;

// Use if-else for complex logic
let status: string;
if (isActive) {
  status = 'active';
} else if (isPending) {
  status = 'pending';
} else {
  status = 'inactive';
}

// Extract to helper function
function getColor(size: string): string {
  if (size === 'large') return 'red';
  if (size === 'medium') return 'blue';
  if (size === 'small') return 'green';
  return 'gray';
}
const color = getColor(size);

// Use object lookup
const colorMap: Record<string, string> = {
  large: 'red',
  medium: 'blue',
  small: 'green',
};
const color = colorMap[size] ?? 'gray';

// Use switch for multiple conditions
function getStatusColor(status: string): string {
  switch (status) {
    case 'success':
      return 'green';
    case 'warning':
      return 'yellow';
    case 'error':
      return 'red';
    default:
      return 'gray';
  }
}

Configuration Examples

Basic Usage

{
  rules: {
    'maintainability/no-nested-ternary': 'warn'
  }
}

Strict Mode

{
  rules: {
    'maintainability/no-nested-ternary': 'error'
  }
}

Refactoring Patterns

Pattern 1: Extract to Variable

// ❌ Before
const message = user.isAdmin
  ? user.isActive
    ? 'Active Admin'
    : 'Inactive Admin'
  : 'Regular User';

// ✅ After
const adminStatus = user.isActive ? 'Active Admin' : 'Inactive Admin';
const message = user.isAdmin ? adminStatus : 'Regular User';

Pattern 2: Extract to Function

// ❌ Before
const discount = isPremium ? (totalAmount > 100 ? 0.2 : 0.1) : 0;

// ✅ After
function calculateDiscount(isPremium: boolean, totalAmount: number): number {
  if (!isPremium) return 0;
  return totalAmount > 100 ? 0.2 : 0.1;
}
const discount = calculateDiscount(isPremium, totalAmount);

Pattern 3: Use Object Lookup

// ❌ Before
const icon =
  status === 'success'
    ? '✓'
    : status === 'error'
      ? '✗'
      : status === 'warning'
        ? '⚠'
        : '•';

// ✅ After
const statusIcons: Record<string, string> = {
  success: '✓',
  error: '✗',
  warning: '⚠',
};
const icon = statusIcons[status] ?? '•';

Pattern 4: Early Returns

// ❌ Before
const getButtonClass = (variant, disabled) =>
  disabled
    ? 'btn-disabled'
    : variant === 'primary'
      ? 'btn-primary'
      : 'btn-secondary';

// ✅ After
function getButtonClass(variant: string, disabled: boolean): string {
  if (disabled) return 'btn-disabled';
  if (variant === 'primary') return 'btn-primary';
  return 'btn-secondary';
}

When Not To Use

ScenarioRecommendation
🎯 Very simple casesStill discouraged but may be acceptable
⚛️ JSX conditionalsConsider component extraction
📊 Type narrowingTypeScript may require specific patterns

Comparison with Alternatives

Featureno-nested-ternaryESLint built-inunicorn
Deep nesting✅ All levels✅ Yes✅ Yes
JSX detection✅ Yes❌ No⚠️ Limited
LLM-Optimized✅ Yes❌ No❌ No
ESLint MCP✅ Optimized❌ No❌ No
Suggestions✅ Yes❌ No⚠️ Limited

Further Reading

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Dynamic Variable References

Why: Static analysis cannot trace values stored in variables or passed through function parameters.

// ❌ NOT DETECTED - Value from variable
const value = externalSource();
processValue(value); // Variable origin not tracked

Mitigation: Implement runtime validation and review code manually. Consider using TypeScript branded types for validated inputs.

Wrapped or Aliased Functions

Why: Custom wrapper functions or aliased methods are not recognized by the rule.

// ❌ NOT DETECTED - Custom wrapper
function myWrapper(data) {
  return internalApi(data); // Wrapper not analyzed
}
myWrapper(unsafeInput);

Mitigation: Apply this rule's principles to wrapper function implementations. Avoid aliasing security-sensitive functions.

Imported Values

Why: When values come from imports, the rule cannot analyze their origin or construction.

// ❌ NOT DETECTED - Value from import
import { getValue } from './helpers';
processValue(getValue()); // Cross-file not tracked

Mitigation: Ensure imported values follow the same constraints. Use TypeScript for type safety.

On this page

No Headings