Claude-skill-registry eslint-flat-config

ESLint 9.x flat configuration patterns. Use when setting up ESLint with TypeScript.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/eslint-flat-config" ~/.claude/skills/majiayu000-claude-skill-registry-eslint-flat-config && rm -rf "$T"
manifest: skills/data/eslint-flat-config/SKILL.md
source content

ESLint Flat Config Skill

This skill covers ESLint 9.x flat configuration for TypeScript projects.

When to Use

Use this skill when:

  • Setting up ESLint for TypeScript projects
  • Migrating from legacy .eslintrc to flat config
  • Configuring strict linting rules
  • Integrating ESLint with Prettier

Core Principle

FLAT CONFIG IS THE FUTURE - ESLint 9.x uses flat config (

eslint.config.ts
) by default.

Basic Configuration

eslint.config.ts

import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import type { Linter } from 'eslint';

const config: Linter.Config[] = [
  // Ignore patterns
  {
    ignores: ['dist/**', 'node_modules/**', 'coverage/**'],
  },

  // Base JavaScript rules
  js.configs.recommended,

  // TypeScript strict rules
  ...tseslint.configs.strictTypeChecked,

  // Custom rules for TypeScript files
  {
    files: ['**/*.ts', '**/*.tsx'],
    languageOptions: {
      parserOptions: {
        project: './tsconfig.json',
        tsconfigRootDir: import.meta.dirname,
      },
    },
    rules: {
      // Type safety
      '@typescript-eslint/no-explicit-any': 'error',
      '@typescript-eslint/no-unsafe-assignment': 'error',
      '@typescript-eslint/no-unsafe-member-access': 'error',
      '@typescript-eslint/no-unsafe-call': 'error',
      '@typescript-eslint/no-unsafe-return': 'error',
      '@typescript-eslint/no-unsafe-argument': 'error',

      // Code quality
      '@typescript-eslint/explicit-function-return-type': 'error',
      '@typescript-eslint/explicit-module-boundary-types': 'error',
      '@typescript-eslint/no-unused-vars': [
        'error',
        { argsIgnorePattern: '^_' },
      ],

      // Strict boolean expressions
      '@typescript-eslint/strict-boolean-expressions': 'error',

      // No floating promises
      '@typescript-eslint/no-floating-promises': 'error',
      '@typescript-eslint/no-misused-promises': 'error',
    },
  },
];

export default config;

Installation

npm install -D eslint @eslint/js typescript-eslint

Package.json Scripts

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint --fix ."
  }
}

Key Rules Explained

Type Safety Rules

// @typescript-eslint/no-explicit-any: 'error'
// ❌ Bad
function process(data: any) { }

// ✅ Good
function process(data: unknown) { }
// @typescript-eslint/explicit-function-return-type: 'error'
// ❌ Bad
function add(a: number, b: number) {
  return a + b;
}

// ✅ Good
function add(a: number, b: number): number {
  return a + b;
}
// @typescript-eslint/strict-boolean-expressions: 'error'
// ❌ Bad
if (value) { }

// ✅ Good
if (value !== undefined && value !== null) { }
if (typeof value === 'string' && value.length > 0) { }

Promise Handling Rules

// @typescript-eslint/no-floating-promises: 'error'
// ❌ Bad - Promise ignored
fetchData();

// ✅ Good - Promise handled
await fetchData();
// or
fetchData().catch(handleError);
// or
void fetchData(); // Explicitly ignored
// @typescript-eslint/no-misused-promises: 'error'
// ❌ Bad - async function in non-async context
const handlers = {
  onClick: async () => { }, // Error in some contexts
};

// ✅ Good - wrap in non-async handler
const handlers = {
  onClick: () => {
    void handleClick();
  },
};

ESLint with Prettier

Installation

npm install -D eslint-config-prettier

Configuration

import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';

const config = [
  js.configs.recommended,
  ...tseslint.configs.strictTypeChecked,
  prettier, // Must be last to override other formatting rules
  {
    // Custom rules...
  },
];

export default config;

File-Specific Configurations

const config = [
  // All TypeScript files
  {
    files: ['**/*.ts', '**/*.tsx'],
    rules: {
      '@typescript-eslint/explicit-function-return-type': 'error',
    },
  },

  // Test files - relaxed rules
  {
    files: ['**/*.test.ts', '**/*.test.tsx', '**/__tests__/**'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      '@typescript-eslint/no-unsafe-assignment': 'off',
    },
  },

  // Config files - CommonJS allowed
  {
    files: ['*.config.js', '*.config.cjs'],
    rules: {
      '@typescript-eslint/no-var-requires': 'off',
    },
  },
];

Custom Rule Configurations

Naming Conventions

{
  rules: {
    '@typescript-eslint/naming-convention': [
      'error',
      // Variables: camelCase
      {
        selector: 'variable',
        format: ['camelCase', 'UPPER_CASE'],
      },
      // Types: PascalCase
      {
        selector: 'typeLike',
        format: ['PascalCase'],
      },
      // Interfaces: PascalCase, no I prefix
      {
        selector: 'interface',
        format: ['PascalCase'],
        custom: {
          regex: '^I[A-Z]',
          match: false,
        },
      },
      // Private members: underscore prefix
      {
        selector: 'memberLike',
        modifiers: ['private'],
        format: ['camelCase'],
        leadingUnderscore: 'require',
      },
    ],
  },
}

Import Organization

npm install -D eslint-plugin-import
import importPlugin from 'eslint-plugin-import';

const config = [
  {
    plugins: {
      import: importPlugin,
    },
    rules: {
      'import/order': [
        'error',
        {
          groups: [
            'builtin',
            'external',
            'internal',
            'parent',
            'sibling',
            'index',
          ],
          'newlines-between': 'always',
          alphabetize: { order: 'asc' },
        },
      ],
      'import/no-duplicates': 'error',
    },
  },
];

Migration from Legacy Config

Old .eslintrc.json

{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "rules": {
    "@typescript-eslint/no-explicit-any": "error"
  }
}

New eslint.config.ts

import js from '@eslint/js';
import tseslint from 'typescript-eslint';

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    rules: {
      '@typescript-eslint/no-explicit-any': 'error',
    },
  },
];

Common Issues and Solutions

"Parsing error: Cannot read file tsconfig.json"

// Ensure tsconfigRootDir is set
{
  languageOptions: {
    parserOptions: {
      project: './tsconfig.json',
      tsconfigRootDir: import.meta.dirname,
    },
  },
}

"Definition for rule not found"

# Ensure all plugins are installed
npm install -D @eslint/js typescript-eslint

Files Not Being Linted

// Check ignores pattern
{
  ignores: ['dist/**', 'node_modules/**'],
}

// Ensure files pattern includes your files
{
  files: ['**/*.ts', '**/*.tsx'],
}

IDE Integration

VS Code Settings

{
  "eslint.useFlatConfig": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}

Best Practices Summary

  1. Use TypeScript for eslint.config.ts
  2. Enable strictTypeChecked rules
  3. Put Prettier config last
  4. Relax rules for test files
  5. Set tsconfigRootDir properly
  6. Use file-specific configurations
  7. Configure VS Code for auto-fix

Code Review Checklist

  • eslint.config.ts uses TypeScript
  • strictTypeChecked rules enabled
  • no-explicit-any set to error
  • explicit-function-return-type enabled
  • Prettier config is last
  • Test files have relaxed rules
  • Ignores dist and node_modules