Lint Cleanup

Fix ESLint errors safely. Prefix unused vars with `_`, fix case declarations, add default props, verify builds. Use for easier refactoring and faster AI editing.

Sby Skills Guide Bot
DevelopmentIntermediate
207/24/2026
Claude Code
#eslint#linting#typescript#vue#cleanup

Recommended for


name: Lint Cleanup description: FIX ESLint errors safely without breaking code. Prefix unused variables with _, fix no-case-declarations, add default props, and verify builds after each batch. Use when lint cleanup is needed for easier refactoring & faster Claude Code editing.

Safe Lint Cleanup

Instructions

This skill provides safe patterns for fixing ESLint errors without breaking code.

Pre-Flight Checklist

BEFORE starting any lint cleanup:

# 1. Get baseline count
npm run lint 2>&1 | tail -5

# 2. Save full output for reference
npm run lint 2>&1 > /tmp/lint-output.txt

# 3. Verify build works
npm run build

# 4. Check git status (ensure clean state)
git status

Error Categories & Fixes

1. Unused Variables (@typescript-eslint/no-unused-vars)

Pattern 1: Prefix Unused Variables with _

This is the SAFEST fix - silences the error without changing logic:

// BEFORE (error)
const unusedVar = someValue

// AFTER (no error)
const _unusedVar = someValue

Pattern 2: Prefix Unused Function Parameters

// BEFORE
const handleClick = (event: MouseEvent) => {
  // doesn't use event
}

// AFTER
const handleClick = (_event: MouseEvent) => {
  // doesn't use event
}

Pattern 3: Destructuring with Rename

// BEFORE
const { unusedProp, usedProp } = options

// AFTER
const { unusedProp: _unusedProp, usedProp } = options

Pattern 4: Unused Type Imports

// BEFORE
import { SomeType, UsedType } from '@/types'

// AFTER
import type { SomeType as _SomeType, UsedType } from '@/types'

Pattern 5: Remove Completely Unused Imports

// BEFORE
import { ref, watch, computed } from 'vue'  // watch never used

// AFTER
import { ref, computed } from 'vue'

2. Case Declarations (no-case-declarations)

Error: Unexpected lexical declaration in case block

Cause: Using const, let, or class directly in a case block without braces.

Fix: Wrap case block contents in {} to create block scope.

// BEFORE (error)
switch (type) {
  case 'foo':
    const value = computeValue()  // ERROR!
    doSomething(value)
    break
  case 'bar':
    const other = computeOther()  // ERROR!
    doOther(other)
    break
}

// AFTER (fixed)
switch (type) {
  case 'foo': {
    const value = computeValue()
    doSomething(value)
    break
  }
  case 'bar': {
    const other = computeOther()
    doOther(other)
    break
  }
}

Quick find pattern:

# Find files with case declarations errors
npm run lint 2>&1 | grep "no-case-declarations" -B1 | grep -E "^/" | sort -u

3. Vue Default Props (vue/require-default-prop)

Error: Prop 'X' requires default value to be set

Cause: Optional props without default values in Vue 3 <script setup>.

Fix: Use withDefaults() to provide default values.

<!-- BEFORE (error) -->
<script setup lang="ts">
interface Props {
  title?: string
  count?: number
  items?: string[]
}
const props = defineProps<Props>()
</script>

<!-- AFTER (fixed) -->
<script setup lang="ts">
interface Props {
  title?: string
  count?: number
  items?: string[]
}
const props = withDefaults(defineProps<Props>(), {
  title: '',
  count: 0,
  items: () => []  // Use factory for arrays/objects
})
</script>

Default value rules:

  • Primitives: Use direct value ('', 0, false, null)
  • Arrays: Use factory function () => []
  • Objects: Use factory function () => ({})

4. Boolean Shorthand (vue/prefer-true-attribute-shorthand)

Error: Boolean attribute should be written in shorthand form

Cause: Using :prop="true" instead of just prop.

<!-- BEFORE (error) -->
<MyComponent :is-active="true" :show-header="true" />

<!-- AFTER (fixed) -->
<MyComponent is-active show-header />

5. Empty Object Types (@typescript-eslint/no-empty-object-type)

Error: The {} type is not recommended

Cause: Using {} as a type in TypeScript (especially in .d.ts files).

Fix: Use object or Record<string, never> instead, OR exclude .d.ts files:

// In eslint.config.js ignores:
ignores: ['**/*.d.ts']

6. ESLint Config Alignment

Problem: Files excluded from tsconfig.json but not from ESLint cause parsing errors.

Fix: Keep ESLint ignores aligned with tsconfig excludes:

// eslint.config.js
{
  ignores: [
    'node_modules/**',
    'dist/**',
    '**/*.d.ts',
    // Match tsconfig.json exclude patterns:
    'src/stories/**',
    'src/composables/adapters/**',
    'src/sync/**',
    'src/utils/emojiDetection.ts',
    'src/utils/logger.ts',
    // etc.
  ]
}

ESLint Config for Underscore Pattern

CRITICAL: The underscore pattern must be configured for BOTH .ts AND .vue files:

// eslint.config.js

// For TypeScript files
{
  files: ['**/*.ts', '**/*.tsx'],
  rules: {
    '@typescript-eslint/no-unused-vars': ['error', {
      argsIgnorePattern: '^_',
      varsIgnorePattern: '^_',
      caughtErrorsIgnorePattern: '^_'
    }]
  }
}

// For Vue files - MUST BE SEPARATE!
{
  files: ['**/*.vue'],
  rules: {
    '@typescript-eslint/no-unused-vars': ['error', {
      argsIgnorePattern: '^_',
      varsIgnorePattern: '^_',
      caughtErrorsIgnorePattern: '^_'
    }]
  }
}

Verification Process

After every batch of fixes:

# 1. Run build
npm run build 2>&1 | tail -10

# 2. Check new lint count
npm run lint 2>&1 | tail -5

# 3. Calculate progress
# (baseline - current) / baseline * 100 = % fixed

Files to Be Careful With

| File Type | Reason | |-----------|--------| | Vue components with templates | Variables might be used in template | | Store files | Exported functions might appear unused | | Composables | Returned values might appear unused | | Test files | May have intentional unused vars | | .d.ts files | Type declarations may use special patterns |


Error Priority Order

Fix errors in this order (highest impact first):

  1. Parsing errors - ESLint ignores alignment with tsconfig
  2. no-case-declarations - Structural fixes, wrap in {}
  3. require-default-prop - Add withDefaults()
  4. no-unused-vars - Prefix with _ or remove
  5. prefer-true-attribute-shorthand - Simple template fixes
  6. no-explicit-any - Requires proper typing (lower priority)

Quick Reference Commands

# Get current lint count
npm run lint 2>&1 | tail -5

# Save full output
npm run lint 2>&1 > /tmp/lint-output.txt

# Find files with specific error
npm run lint 2>&1 | grep "ERROR_NAME" -B1 | grep -E "^/" | sort -u

# Count specific error type
grep "ERROR_NAME" /tmp/lint-output.txt | wc -l

# Check specific file errors
cat /tmp/lint-output.txt | grep -A20 "YourFile.vue$" | head -25

# Run --fix for auto-fixable errors (safe)
npm run lint -- --fix

# Find case declarations by file
awk '/^\/home.*\.(vue|ts)$/ { file=$0 } /no-case-declarations/ { print file }' /tmp/lint-output.txt | sort -u

Common Gotchas

  1. Multiple Matches: Add more surrounding context to make match unique
  2. Variable Used in Template: Check <template> section before prefixing
  3. Exported but Appears Unused: Don't prefix exports - used externally
  4. Store/Composable Return Values: Check if values are returned for consumers
  5. Case Block Fallthrough: Ensure break is inside the {} braces
  6. Factory Functions for Props: Arrays/objects need () => [] not []

See lint-cleanup-guide.md for complete documentation.

Related skills