Project Setup — ESLint, Prettier, TypeScript, Husky
ESLint flat config (v9+)
Заголовок раздела «ESLint flat config (v9+)»С ESLint v9 конфигурация переехала в eslint.config.js (flat config). Старый .eslintrc.* — deprecated.
Установка
Заголовок раздела «Установка»npm install -D \ eslint \ @eslint/js \ typescript-eslint \ eslint-plugin-react \ eslint-plugin-react-hooks \ eslint-plugin-react-refresh \ globalseslint.config.js
Заголовок раздела «eslint.config.js»import js from '@eslint/js';import globals from 'globals';import reactHooks from 'eslint-plugin-react-hooks';import reactRefresh from 'eslint-plugin-react-refresh';import tseslint from 'typescript-eslint';
export default tseslint.config( // Ignore patterns { ignores: ['dist', 'node_modules', '*.config.js'] },
// Base JS rules js.configs.recommended,
// TypeScript rules ...tseslint.configs.recommendedTypeChecked, { languageOptions: { parserOptions: { project: true, tsconfigRootDir: import.meta.dirname, }, }, },
// React rules { files: ['**/*.{ts,tsx}'], plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh, }, languageOptions: { globals: { ...globals.browser }, }, rules: { // React Hooks ...reactHooks.configs.recommended.rules,
// React Refresh (Vite HMR) 'react-refresh/only-export-components': [ 'warn', { allowConstantExport: true }, ],
// TypeScript '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, ], '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/prefer-nullish-coalescing': 'error', '@typescript-eslint/prefer-optional-chain': 'error', '@typescript-eslint/no-floating-promises': 'error',
// General 'no-console': ['warn', { allow: ['warn', 'error'] }], 'prefer-const': 'error', }, });package.json scripts
Заголовок раздела «package.json scripts»{ "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix" }}Prettier
Заголовок раздела «Prettier»npm install -D prettier eslint-config-prettier.prettierrc
Заголовок раздела «.prettierrc»{ "semi": true, "singleQuote": true, "trailingComma": "es5", "tabWidth": 2, "printWidth": 100, "bracketSpacing": true, "arrowParens": "avoid", "endOfLine": "lf", "importOrder": [ "^react", "^react-dom", "<THIRD_PARTY_MODULES>", "^@/(.*)$", "^[./]" ]}.prettierignore
Заголовок раздела «.prettierignore»distnode_modules*.lockИнтеграция ESLint + Prettier
Заголовок раздела «Интеграция ESLint + Prettier»// eslint.config.js — добавить в конецimport prettierConfig from 'eslint-config-prettier';
export default tseslint.config( // ... остальные правила prettierConfig // отключает ESLint правила, конфликтующие с Prettier);TypeScript strict mode
Заголовок раздела «TypeScript strict mode»Полный набор строгих опций в tsconfig.json:
{ "compilerOptions": { /* Strict режим — включает все ниже */ "strict": true,
/* Составляющие strict: */ "strictNullChecks": true, // null/undefined не присваиваются другим типам "strictFunctionTypes": true, // strict covariance для параметров функций "strictPropertyInitialization": true, // свойства класса должны быть инициализированы "noImplicitAny": true, // запрет неявного any "noImplicitThis": true, // запрет this с типом any
/* Дополнительно рекомендуется: */ "noUnusedLocals": true, // ошибка на неиспользуемые переменные "noUnusedParameters": true, // ошибка на неиспользуемые параметры "exactOptionalPropertyTypes": true, // отличие undefined от optional "noUncheckedIndexedAccess": true, // индекс возвращает T | undefined "noImplicitOverride": true // явный override при переопределении методов }}Частые паттерны со strict
Заголовок раздела «Частые паттерны со strict»// ❌ Без strictfunction getUser(id: string) { return users.find(u => u.id === id); // User | undefined — но TypeScript не предупредит}const user = getUser('123');console.log(user.name); // runtime error!
// ✅ Со strictfunction getUser(id: string): User | undefined { return users.find(u => u.id === id);}const user = getUser('123');console.log(user?.name); // TypeScript требует optional chaining
// noUncheckedIndexedAccessconst arr = [1, 2, 3];const item = arr[10]; // тип: number | undefined, не numberif (item !== undefined) { console.log(item * 2); // OK}Husky + lint-staged
Заголовок раздела «Husky + lint-staged»Pre-commit хуки — запускают линтер только на staged файлах.
Установка
Заголовок раздела «Установка»npm install -D husky lint-staged
# Инициализация huskynpx husky initpackage.json
Заголовок раздела «package.json»{ "scripts": { "prepare": "husky" }, "lint-staged": { "*.{ts,tsx}": [ "eslint --fix", "prettier --write" ], "*.{js,json,css,md}": [ "prettier --write" ] }}.husky/pre-commit
Заголовок раздела «.husky/pre-commit»npx lint-staged.husky/commit-msg (опционально — Conventional Commits)
Заголовок раздела «.husky/commit-msg (опционально — Conventional Commits)»npx --no -- commitlint --edit "$1"# Установка commitlintnpm install -D @commitlint/config-conventional @commitlint/cli
# commitlint.config.tsexport default { extends: ['@commitlint/config-conventional'],};Формат коммитов: type(scope): description
feat: add user profile pagefix(auth): handle expired tokensrefactor(stores): simplify UserStore
Итоговая структура конфигов
Заголовок раздела «Итоговая структура конфигов»my-app/ .husky/ pre-commit # npx lint-staged src/ eslint.config.js # ESLint flat config v9 .prettierrc # Prettier настройки .prettierignore tsconfig.json # strict TypeScript commitlint.config.ts # optional package.json # lint-staged config