ESLint and Prettier Setup

Intermediate
11 min

ESLint and Prettier Setup

The compiler catches type errors; it does not catch an unawaited promise, an unused import, an any that slipped in, or inconsistent formatting across a team. ESLint with the typescript-eslint plugin covers the first group, including rules that use type information, and Prettier settles formatting so nobody argues about it in reviews. This lesson installs both, configures them for a TypeScript project using the current flat config format, and wires them into the editor and CI.

Installing ESLint for TypeScript

bash
npm install --save-dev eslint @eslint/js typescript-eslint

ESLint 9 uses a flat config file, eslint.config.mjs, instead of the legacy .eslintrc. The minimal configuration is three lines:

javascript
import eslint from "@eslint/js"; import tseslint from "typescript-eslint"; import { defineConfig } from "eslint/config"; export default defineConfig(eslint.configs.recommended, tseslint.configs.recommended);

typescript-eslint bundles the parser (so ESLint can read TypeScript syntax) and the plugin (the rules). Run it with npx eslint .; add "lint": "eslint ." and "lint:fix": "eslint . --fix" to package.json scripts.

Type-Aware Rules

The most valuable rules need the type checker. Switch from recommended to recommendedTypeChecked and tell the parser where your tsconfig.json is, as in the sample at the top. projectService: true lets typescript-eslint discover the nearest tsconfig for each file automatically. Rules worth enabling explicitly:

| Rule | Catches | |---|---| | no-floating-promises | A promise that is neither awaited, returned nor handled | | no-misused-promises | async functions passed where a void callback is expected (for example forEach) | | no-unnecessary-condition | Checks that are always true or false according to the types | | switch-exhaustiveness-check | A switch over a union that misses a member | | consistent-type-imports | Type imports written without import type | | no-explicit-any | Explicit any annotations (in recommended) | | no-unused-vars | Unused variables and imports, with _-prefixed exceptions |

Type-aware linting is slower because it loads the TypeScript program. On large repositories, keep type-checked rules for CI and editor use, and consider tseslint.configs.strictTypeChecked only once the codebase is clean.

For test files or scripts outside tsconfig.json, disable the type-checked rules with a scoped block:

javascript
{ files: ["**/*.js", "scripts/**"], extends: [tseslint.configs.disableTypeChecked], }

Adding Prettier

Prettier is an opinionated formatter; it rewrites code to a canonical style and has almost no options to debate. Install it together with the package that disables ESLint's conflicting formatting rules:

bash
npm install --save-dev prettier eslint-config-prettier
json
// .prettierrc { "semi": true, "singleQuote": false, "trailingComma": "all", "printWidth": 100 }

Add eslintConfigPrettier as the last entry of eslint.config.mjs so it wins over any formatting rule, then add scripts:

json
{ "scripts": { "format": "prettier --write .", "format:check": "prettier --check ." } }

A .prettierignore (with dist, coverage, node_modules) keeps generated files untouched. Prettier formats .ts, .tsx, .json, .md and .css files out of the box.

Editor and CI Integration

In VS Code, install the ESLint and Prettier extensions and add to .vscode/settings.json:

json
{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } }

Every save now formats the file and applies ESLint autofixes such as adding import type. In CI, run the three checks separately so failures are easy to read:

bash
npm run typecheck # tsc --noEmit npm run lint # eslint . npm run format:check # prettier --check .

Teams that want the checks before code even reaches CI add husky and lint-staged to run eslint --fix and prettier --write on staged files in a pre-commit hook.

Common mistakes

  • Placing eslintConfigPrettier before other configs, which lets later configs re-enable conflicting rules.
  • Enabling type-checked rules without projectService or project, which produces "parserOptions.project has not been set" errors.
  • Using ESLint to format code. Let Prettier own formatting and ESLint own correctness.
Quick Quiz
Question 1 of 3

What is the name of the ESLint 9 configuration file format used with typescript-eslint?

Key Takeaways

  • Install eslint, @eslint/js and typescript-eslint; configure them in a flat eslint.config.mjs.
  • Use recommendedTypeChecked with projectService: true to unlock rules such as no-floating-promises.
  • Prettier owns formatting; eslint-config-prettier (placed last) stops ESLint from fighting it.
  • Wire format-on-save and ESLint autofix into the editor, and run tsc, eslint and prettier --check in CI.
  • Scope out non-TypeScript files with disableTypeChecked instead of turning rules off globally.

Next lesson: Build Tools: tsc, esbuild, tsx and Vite — choose the right compiler and bundler for each kind of project.

ESLint and Prettier Setup - TypeScript | CodeYourCraft | CodeYourCraft