ESLint Setup
ESLint Setup
Installation
npm install --save-dev eslint @eslint/js typescript-eslint
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
eslint.config.js (Flat Config)
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
prettier,
{
rules: {
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/explicit-function-return-type': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/strict-boolean-expressions': 'off',
'no-console': ['warn', { allow: ['warn', 'error'] }]
}
},
{
ignores: ['dist/', 'node_modules/', '*.js']
}
);
package.json Scripts
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}
TypeScript ESLint Rules
TypeScript ESLint Rules
Recommended Rules
// @typescript-eslint/no-unused-vars
function processData(data: string, _options: Options) {
// _options is allowed because of argsIgnorePattern
return data.toUpperCase();
}
// @typescript-eslint/explicit-function-return-type
// Bad
const double = (x: number) => x * 2;
// Good
const double = (x: number): number => x * 2;
// @typescript-eslint/no-explicit-any
// Bad
function process(input: any) {
return input;
}
// Good
function process<T>(input: T): T {
return input;
}
Strict Rules
// @typescript-eslint/consistent-type-imports
// Bad
import { User } from './types';
// Good
import type { User } from './types';
// @typescript-eslint/no-floating-promises
// Bad
fetchData(); // Promise not handled
// Good
await fetchData();
// or
fetchData().catch(console.error);
// @typescript-eslint/await-thenable
// Bad
await 'hello'; // not a thenable
// Good
await Promise.resolve('hello');
Prettier Configuration
Prettier Configuration
.prettierrc
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf"
}
Integration with ESLint
// eslint-config-prettier disables all ESLint rules that conflict with Prettier
// eslint-plugin-prettier runs Prettier as an ESLint rule
// .eslintrc.js
{
"extends": [
"plugin:prettier/recommended"
]
}
Editor Integration
// .vscode/settings.json
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
Pre-Commit Hooks
Pre-Commit Hooks
Husky and lint-staged
npm install --save-dev husky lint-staged
# Initialize Husky
npx husky init
# Add pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"
lint-staged Configuration
// package.json
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yml}": [
"prettier --write"
]
}
}
.husky/pre-commit
#!/bin/sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
CI Integration
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run lint
- run: npm run format:check
- run: npm run typecheck
- run: npm test
Best Practices
- Run
eslint --fixbefore committing - Use
prettier --checkin CI to enforce formatting - Configure editor for real-time linting feedback
- Use
typecheckscript in CI for type safety - Keep linting rules consistent across the team