Skip to content
beginner Phase 7 · TypeScript Testing & Tools

ESLint & Prettier

Enforce code quality with ESLint rules and format with Prettier.

30m
0 problems
Topic Progress 0%

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 --fix before committing
  • Use prettier --check in CI to enforce formatting
  • Configure editor for real-time linting feedback
  • Use typecheck script in CI for type safety
  • Keep linting rules consistent across the team