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.
npm install --save-dev eslint @eslint/js typescript-eslintESLint 9 uses a flat config file, eslint.config.mjs, instead of the legacy .eslintrc. The minimal configuration is three lines:
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.
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:
{
files: ["**/*.js", "scripts/**"],
extends: [tseslint.configs.disableTypeChecked],
}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:
npm install --save-dev prettier eslint-config-prettier// .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:
{
"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.
In VS Code, install the ESLint and Prettier extensions and add to .vscode/settings.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:
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.
eslintConfigPrettier before other configs, which lets later configs re-enable conflicting rules.projectService or project, which produces "parserOptions.project has not been set" errors.What is the name of the ESLint 9 configuration file format used with typescript-eslint?
eslint, @eslint/js and typescript-eslint; configure them in a flat eslint.config.mjs.recommendedTypeChecked with projectService: true to unlock rules such as no-floating-promises.eslint-config-prettier (placed last) stops ESLint from fighting it.tsc, eslint and prettier --check in CI.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.