ESLint 9 & Biome Linting
When NOT to Use This Skill
-
Legacy ESLint (.eslintrc) - Use eslint skill for old config format
-
TypeScript-only rules - Use typescript-eslint skill for deep TypeScript linting
-
Java/Kotlin linting - Use sonarqube skill
-
Code quality principles - Use quality-common for SOLID/Clean Code
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: eslint or biome for comprehensive documentation.
Official References
Tool Documentation
ESLint 9 https://eslint.org/docs/latest/
typescript-eslint https://typescript-eslint.io/
Biome https://biomejs.dev/
ESLint Rules https://eslint.org/docs/latest/rules/
Biome Rules https://biomejs.dev/linter/rules/
ESLint 9 Flat Config
Basic Setup
npm install --save-dev eslint @eslint/js typescript typescript-eslint
// eslint.config.mjs import eslint from '@eslint/js'; import tseslint from 'typescript-eslint';
export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, );
TypeScript with Type Checking
// eslint.config.mjs import eslint from '@eslint/js'; import tseslint from 'typescript-eslint';
export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, { languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }, );
Full Configuration Example
// eslint.config.mjs import eslint from '@eslint/js'; import tseslint from 'typescript-eslint'; import reactPlugin from 'eslint-plugin-react'; import reactHooksPlugin from 'eslint-plugin-react-hooks';
export default tseslint.config( // Ignores { ignores: ['dist/', 'node_modules/', '*.config.js'], },
// Base configs eslint.configs.recommended, ...tseslint.configs.strictTypeChecked, ...tseslint.configs.stylisticTypeChecked,
// TypeScript files { files: ['/*.ts', '/*.tsx'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, rules: { // Prevent bugs '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': 'error', '@typescript-eslint/await-thenable': 'error',
// Code quality
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/explicit-function-return-type': 'warn',
'@typescript-eslint/consistent-type-imports': 'error',
// Complexity
'complexity': ['warn', 10],
'max-depth': ['warn', 4],
'max-lines-per-function': ['warn', 50],
},
},
// React files { files: ['**/*.tsx'], plugins: { react: reactPlugin, 'react-hooks': reactHooksPlugin, }, rules: { ...reactPlugin.configs.recommended.rules, ...reactHooksPlugin.configs.recommended.rules, 'react/react-in-jsx-scope': 'off', }, settings: { react: { version: 'detect' }, }, },
// Test files { files: ['/*.test.ts', '/.test.tsx', '**/.spec.ts'], rules: { '@typescript-eslint/no-explicit-any': 'off', 'max-lines-per-function': 'off', }, }, );
typescript-eslint Presets
Preset Description
recommended
Core rules without type checking
recommendedTypeChecked
Recommended + type-aware rules
strict
All recommended + stricter rules
strictTypeChecked
Strict + type-aware rules
stylistic
Style/convention rules
stylisticTypeChecked
Stylistic + type-aware
Key Type-Checked Rules
rules: { // Async/Promise safety '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': 'error', '@typescript-eslint/await-thenable': 'error', '@typescript-eslint/require-await': 'error',
// Type safety '@typescript-eslint/no-unsafe-argument': 'error', '@typescript-eslint/no-unsafe-assignment': 'error', '@typescript-eslint/no-unsafe-call': 'error', '@typescript-eslint/no-unsafe-member-access': 'error', '@typescript-eslint/no-unsafe-return': 'error',
// Best practices '@typescript-eslint/no-unnecessary-condition': 'error', '@typescript-eslint/prefer-nullish-coalescing': 'error', '@typescript-eslint/prefer-optional-chain': 'error', }
Biome
Installation
npm install --save-dev @biomejs/biome npx @biomejs/biome init
Configuration (biome.json)
{ "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", "organizeImports": { "enabled": true }, "linter": { "enabled": true, "rules": { "recommended": true, "complexity": { "noExcessiveCognitiveComplexity": { "level": "warn", "options": { "maxAllowedComplexity": 15 } } }, "correctness": { "noUnusedImports": "error", "noUnusedVariables": "error", "useExhaustiveDependencies": "warn" }, "suspicious": { "noExplicitAny": "error", "noConsoleLog": "warn" }, "style": { "useConst": "error", "noNonNullAssertion": "warn" }, "security": { "noDangerouslySetInnerHtml": "error" } } }, "formatter": { "enabled": true, "indentStyle": "space", "indentWidth": 2 }, "javascript": { "formatter": { "quoteStyle": "single", "semicolons": "always" } } }
Rule Categories
Category Rules Description
a11y 40+ Accessibility problems
complexity 15+ Code simplification
correctness 60+ Guaranteed errors
performance 10+ Efficiency improvements
security 5+ Security flaws
style 50+ Consistent code style
suspicious 50+ Likely incorrect patterns
nursery 100+ Experimental rules
Commands
Check all
npx biome check .
Fix auto-fixable issues
npx biome check --write .
Lint only
npx biome lint .
Format only
npx biome format --write .
CI mode (no writes)
npx biome ci .
Migration: ESLint to Biome
Automatic migration
npx @biomejs/biome migrate eslint --write
With inspired rules
npx @biomejs/biome migrate eslint --include-inspired --write
Migrate prettier config too
npx @biomejs/biome migrate prettier --write
Supported ESLint Plugins
Plugin Biome Support
@typescript-eslint Full
eslint-plugin-react Full
eslint-plugin-react-hooks Full
eslint-plugin-jsx-a11y Full
eslint-plugin-unicorn Partial
eslint-plugin-import Partial
Rule Name Mapping
ESLint Biome
no-unused-vars
noUnusedVariables
no-console
noConsoleLog
prefer-const
useConst
@typescript-eslint/no-explicit-any
noExplicitAny
ESLint vs Biome
Feature ESLint 9 Biome
Speed ~3-5s/10k lines ~200ms/10k lines
Config JavaScript JSON/JSONC
Plugins 1000+ Built-in only
Formatter Needs Prettier Built-in
Type-aware Full support ~85% coverage
Languages JS/TS + plugins JS/TS/JSON/CSS
When to Use ESLint
-
Need specific plugins (import sorting, testing rules)
-
Require full type-aware linting
-
Have complex dynamic configuration
When to Use Biome
-
Speed is critical (large codebase, CI)
-
Want unified linting + formatting
-
Don't need extensive plugin ecosystem
CI Integration
ESLint (GitHub Actions)
- name: Lint run: npx eslint . --max-warnings=0
With caching
- name: Lint with cache run: npx eslint . --cache --cache-location node_modules/.cache/eslint
Biome (GitHub Actions)
-
name: Setup Biome uses: biomejs/setup-biome@v2 with: version: latest
-
name: Run Biome run: biome ci .
Checklist
ESLint Setup
-
Using flat config (eslint.config.mjs)
-
Type-checked rules enabled
-
No deprecated eslintrc format
-
Strict TypeScript rules
-
CI caching configured
Biome Setup
-
biome.json configured
-
Recommended rules enabled
-
Formatter settings match team style
-
CI mode in pipeline
Metrics
Metric Target
Lint warnings 0
Lint errors 0
Type coverage
95%
Lint time (CI) < 30s
Anti-Patterns
Anti-Pattern Why It's Bad Correct Approach
Using ESLint without type checking Misses 50% of TS issues Use recommendedTypeChecked
Biome + ESLint both linting same files Duplicate work, conflicts Choose one per file type
No ignores in flat config Lints dist/, node_modules/ Add ignores at top of config
Type checking all JS files Slow, JS has no types Limit to **/*.ts files only
Not using Biome CI mode Different results locally vs CI Use biome ci . in pipelines
Mixing .eslintrc and flat config Confusing, deprecated pattern Migrate fully to flat config
Quick Troubleshooting
Issue Likely Cause Solution
ESLint not finding types projectService not enabled Add projectService: true to parserOptions
Biome migration creates 1000+ errors Stricter rules than ESLint Use --write to auto-fix, adjust rules
Linting takes 2+ minutes Type checking entire codebase Use files: ['**/*.ts'] for type rules
Biome doesn't support plugin X Limited plugin ecosystem Stick with ESLint or find alternative
Flat config not recognized Wrong file name Must be eslint.config.mjs (not .js)
Rules overriding each other Order in config array Later configs override earlier ones
Related Skills
-
TypeScript
-
Quality Principles
-
GitHub Actions