Модуль 12.1
ESLint и Prettier с TypeScript
Профессиональный коддинг: автоматизация качества кода
Введение
Добро пожаловать в урок по инструментам автоматизации качества кода! Сегодня мы разберём два essential инструмента, которые обязан использовать каждый TypeScript-разработчик — ESLint и Prettier.
Представьте, что вы работаете в команде из 10 человек. Каждый пишет код по-своему: кто-то использует табуляцию, кто-то пробелы; кто-то ставит точку с запятой, кто-то нет; кто-то именует переменные через camelCase, а кто-то через snake_case. Без единого стандарта кодовая база превращается в хаос.
ESLint и Prettier решают эту проблему автоматически. ESLint находит ошибки и потенциальные проблемы в коде, а Prettier форматирует его по единым правилам. Вместе они обеспечивают консистентность, читаемость и надёжность вашего кода.
1. Что такое ESLint
1.1 История и назначение
ESLint появился в 2013 году как ответ на limitations существовавших тогда линтеров — JSHint и JSLint. Его создатель Николас К. Закас (Nicholas C. Zakas) хотел создать инструмент, который был бы:
- Плагинируемый — любое правило можно добавить, изменить или отключить
- Расширяемый — поддержка кастомных правил и плагинов
- Информативный — понятные сообщения об ошибках с предложениями по исправлению
- Быстрый — оптимизированная архитектура для работы с большими кодовыми базами
1.2 Архитектура ESLint
Внутренняя архитектура ESLint состоит из нескольких ключевых компонентов:
Компоненты архитектуры:
- Parser — преобразует исходный код в AST (Abstract Syntax Tree)
- AST Traverser — обходит дерево синтаксического анализа
- Rule System — набор правил для проверки AST узлов
- Reporter — форматирует и выводит результаты
- Config System — управление конфигурациями и наследованием
// AST (Abstract Syntax Tree) — дерево синтаксического анализа
// Исходный код: const x: number = 42;
// AST узлы:
{
type: "VariableDeclaration",
kind: "const",
declarations: [{
type: "VariableDeclarator",
id: { type: "Identifier", name: "x", typeAnnotation: { type: "TSTypeAnnotation", typeAnnotation: { type: "TSNumberKeyword" } } },
init: { type: "NumericLiteral", value: 42 }
}]
}
// ESLint обходит каждый узел и проверяет правила
1.3 Типы правил ESLint
ESLint предоставляет три категории правил:
Потенциальные проблемы
no-unused-vars, no-console
Критические проблемы
no-undef, semi
Стилистические замечания
no-trailing-spaces
2. Установка и настройка ESLint
2.1 Установка в TypeScript проекте
# Установка ESLint и необходимых пакетов npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin # Или с использованием yarn yarn add --dev eslint @typescript-eslint/parser @typescript-eslint/eslintplugin # Для ESLint 9.x (flat config) — рекомендуемый способ npm install --save-dev eslint typescript-eslint @eslint/js # Автоматическая настройка через CLI npx eslint --init
2.2 Flat Config (ESLint 9.x)
Начиная с ESLint 9.x, рекомендуется использовать формат конфигурации flat config (файл eslint.config.js). Это более простой и гибкий формат по сравнению с legacy .eslintrc:
// eslint.config.js — Flat Config формат (ESLint 9.x)
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettierConfig from 'eslint-config-prettier';
export default tseslint.config(
// Глобальные рекомендации
eslint.configs.recommended,
// Рекомендации для TypeScript
...tseslint.configs.recommended,
// Отключение правил, конфликтующих с Prettier
prettierConfig,
// Кастомные правила проекта
{
rules: {
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
}],
'@typescript-eslint/explicit-function-return-type': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/consistent-type-imports': 'error',
'no-console': ['warn', { allow: ['warn', 'error'] }],
'prefer-const': 'error',
'no-var': 'error',
},
languageOptions: {
parserOptions: {
project: './tsconfig.json',
ecmaVersion: 'latest',
sourceType: 'module',
},
},
},
// Игнорируемые файлы
{
ignores: [
'dist/**',
'node_modules/**',
'coverage/**',
'*.config.js',
'*.config.mjs',
],
}
);
2.3 Legacy Config (.eslintrc)
Для проектов, использующих ESLint 8.x и ниже, конфигурация выглядит так:
// .eslintrc.json — Legacy формат (ESLint 8.x)
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier"
],
"rules": {
"@typescript-eslint/no-unused-vars": ["error", {
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}],
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/consistent-type-imports": "error",
"no-console": ["warn", { "allow": ["warn", "error"] }],
"prefer-const": "error",
"no-var": "error",
"eqeqeq": ["error", "always"],
"no-duplicate-imports": "error"
},
"ignorePatterns": ["dist/**", "node_modules/**", "coverage/**"],
"env": {
"browser": true,
"node": true,
"es2022": true
}
}
2.4 Важные правила для TypeScript
| Правило | Описание | Рекомендуемое значение |
|---|---|---|
@typescript-eslint/no-explicit-any |
Запрещает использование any |
"warn" или "error" |
@typescript-eslint/no-unused-vars |
Запрещает неиспользуемые переменные | "error" с паттернами |
@typescript-eslint/explicit-function-return-type |
Требует явный возвращаемый тип | "warn" |
@typescript-eslint/consistent-type-imports |
Единый стиль импорта типов | "error" |
@typescript-eslint/no-non-null-assertion |
Запрещает ! assertion |
"warn" |
@typescript-eslint/prefer-nullish-coalescing |
Предпочитает ?? вместо || |
"warn" |
@typescript-eslint/prefer-optional-chain |
Предпочитает ?. вместо && |
"error" |
3. Плагины ESLint для TypeScript
3.1 @typescript-eslint — официальная поддержка
Пакет @typescript-eslint состоит из двух основных компонентов:
// @typescript-eslint/parser — парсер TypeScript для ESLint
// Преобразует TypeScript код в AST, понятный ESLint
// @typescript-eslint/eslintplugin — правила для TypeScript
// Содержит hundreds правил, специфичных для TypeScript
// Пример использования расширенных правил:
{
"extends": [
"plugin:@typescript-eslint/recommended", // Базовые правила
"plugin:@typescript-eslint/recommended-requiring-type-checking" // Правила, требующие информацию о типах
],
"rules": {
// Правила для типизации
"@typescript-eslint/ban-types": "error",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/explicit-module-boundary-types": "warn",
"@typescript-eslint/no-inferrable-types": "error",
// Правила для производительности
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/prefer-for-of": "warn",
// Правила для читаемости
"@typescript-eslint/naming-convention": ["error",
{ "selector": "default", "format": ["camelCase"] },
{ "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "parameter", "format": ["camelCase"], "leadingUnderscore": "allow" },
{ "selector": "typeLike", "format": ["PascalCase"] },
{ "selector": "enumMember", "format": ["PascalCase"] }
]
}
}
3.2 Кастомные правила
Вы можете создавать собственные правила ESLint для специфических потребностей вашего проекта:
// eslint-plugin-custom/rules/no-console-log.js
// Кастомное правило: запрет console.log (но разрешает console.warn/error)
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Запрещает использование console.log',
category: 'Best Practices',
recommended: true,
},
fixable: null,
schema: [],
messages: {
noConsoleLog: 'Используйте logger вместо console.log',
},
},
create(context) {
return {
CallExpression(node) {
const { callee } = node;
if (
callee.type === 'MemberExpression' &&
callee.object.type === 'Identifier' &&
callee.object.name === 'console' &&
callee.property.type === 'Identifier' &&
callee.property.name === 'log'
) {
context.report({
node,
messageId: 'noConsoleLog',
});
}
},
};
},
};
// Использование в конфиге:
// "custom/no-console-log": "error"
3.3 Популярные плагины
Проверка импортов, обнаружение дубликатов, сортировка импортов
Правила для React компонентов и JSX
Правила для тестов Jest
Проверка доступности JSX
Множество полезных правил для современного JS/TS
Функциональный стиль программирования
4. Интеграция ESLint с IDE
4.1 Настройка VS Code
// .vscode/settings.json — Настройки проекта
{
// Автоматическое исправление при сохранении
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "explicit"
},
// Показ ошибок ESLint
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
// Форматирование по умолчанию
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
// Отключение встроенного линтера TypeScript
"typescript.tsserver.enableJavascript": true
}
// .vscode/extensions.json — Рекомендуемые расширения
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"csstools.postcss"
]
}
4.2 Настройка JetBrains IDE (WebStorm, IntelliJ)
В JetBrains IDE интеграция ESLint встроена и не требует дополнительных расширений:
- Откройте Settings → Languages & Frameworks → JavaScript → Code Quality Tools → ESLint
- Выберите "Automatic ESLint configuration"
- Укажите путь к конфигурационному файлу
- Включите "Fix ESLint errors on save"
- Настройте "Run eslint --fix on save" для автоматического исправления
4.3 Git Hooks с Husky и lint-staged
Автоматическая проверка кода перед коммитом — essential практика:
# Установка Husky и lint-staged
npm install --save-dev husky lint-staged
# Инициализация Husky
npx husky init
# Создание pre-commit хука
echo "npx lint-staged" > .husky/pre-commit
// package.json — Конфигурация lint-staged
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix --max-warnings=0",
"prettier --write",
"bash -c 'tsc --noEmit'"
],
"*.{js,jsx}": [
"eslint --fix --max-warnings=0",
"prettier --write"
],
"*.{json,md,yml,yaml,css,scss}": [
"prettier --write"
]
},
"scripts": {
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
"typecheck": "tsc --noEmit",
"validate": "npm run typecheck && npm run lint && npm run format:check"
}
}
5. Что такое Prettier
5.1 Назначение и философия
Prettier — это "opinionated" код-форматтер (форматер с жёсткими мнениями). Его философия проста: все форматирующие решения уже приняты за вас. Вы не можете настроить Prettier так, чтобы он форматировал код так же, как вы пишете — вместо этого вы приспосабливаетесь к его стилю.
- Нулевая настройка — работает "из коробки"
- Единый стиль для всей команды
- Больше времени на написание кода, меньше на обсуждение стиля
- Поддерживает множество языков (JS, TS, CSS, HTML, JSON, MD и др.)
5.2 Отличия от ESLint
ESLint
- Находит ошибки и потенциальные проблемы
- Проверяет логику кода
- Настраиваемые правила
- Фокус на качестве кода
- Может исправлять (fixable правила)
Prettier
- Форматирует код по единым правилам
- Не анализирует логику
- Минимальная настройка
- Фокус на стиле и читаемости
- Всегда исправляет (переформатирует)
5.3 Что форматирует Prettier
// ДО форматирования Prettier:
function calculateTotal( items: Array< Product > ): number {
return items.reduce(( sum, item ) => { return sum + item.price * item.quantity; }, 0 )
}
interface User { id: number ; name: string; email:string; role: 'admin' | 'user' | 'moderator' }
const users = [ { id:1, name:'Alice', email:'alice@example.com', role:'admin' }, { id:2, name:'Bob', email:'bob@example.com', role:'user' } ]
// ПОСЛЕ форматирования Prettier:
function calculateTotal(items: Array): number {
return items.reduce((sum, item) => {
return sum + item.price * item.quantity;
}, 0);
}
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user' | 'moderator';
}
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com', role: 'admin' },
{ id: 2, name: 'Bob', email: 'bob@example.com', role: 'user' },
];
6. Настройка Prettier с TypeScript
6.1 Установка
# Установка Prettier npm install --save-dev prettier # Дополнительные плагины npm install --save-dev prettier-plugin-organize-imports # Сортировка импортов npm install --save-dev prettier-plugin-tailwindcss # Сортировка Tailwind классов
6.2 Конфигурация .prettierrc
// .prettierrc — Конфигурация Prettier
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf",
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"htmlWhitespaceSensitivity": "css",
"embeddedLanguageFormatting": "auto",
"singleAttributePerLine": false,
"overrides": [
{
"files": "*.ts",
"options": {
"printWidth": 120,
"trailingComma": "all"
}
},
{
"files": ["*.json", "*.yml", "*.yaml"],
"options": {
"singleQuote": false
}
},
{
"files": "*.md",
"options": {
"printWidth": 80,
"proseWrap": "always"
}
}
]
}
// .prettierignore — Игнорируемые файлы
node_modules
dist
build
coverage
*.min.js
*.min.css
package-lock.json
yarn.lock
pnpm-lock.yaml
6.3 CLI команды Prettier
# Форматирование всех файлов npx prettier --write . # Форматирование конкретных файлов npx prettier --write "src/**/*.ts" # Проверка без форматирования (для CI/CD) npx prettier --check . # Форматирование одного файла npx prettier --write src/index.ts # Форматирование с выводом в stdout npx prettier --parser typescript src/index.ts # Использование конфигурации проекта npx prettier --config .prettierrc --write .
7. Совместная работа ESLint и Prettier
7.1 Почему возникают конфликты
Проблема в том, что и ESLint, и Prettier могут пытаться форматировать один и тот же код. Например, ESLint может требовать точку с запятой, а Prettier — убирать её. Это создаёт конфликт и " fight" между инструментами.
7.2 Решение: eslint-config-prettier
# Установка
npm install --save-dev eslint-config-prettier
// ESLint конфигурация — подключение prettier
// Flat Config (ESLint 9.x):
import prettierConfig from 'eslint-config-prettier';
export default [
...tseslint.configs.recommended,
prettierConfig, // Должен быть ПОСЛЕ других extends!
{ /* ваши правила */ }
];
// Legacy Config (.eslintrc):
{
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier" // Должен быть ПОСЛЕ других extends!
]
}
// eslint-config-prettier автоматически отключает все правила ESLint,
// которые конфликтуют с Prettier:
// - indent
// - semi
// - quotes
// - comma-dangle
// - no-unexpected-multiline
// ... и многие другие
7.3 eslint-plugin-prettier
Этот плагин запускает Prettier как правило ESLint, что позволяет видеть ошибки форматирования прямо в IDE:
# Установка
npm install --save-dev eslint-plugin-prettier
// Конфигурация с eslint-plugin-prettier
{
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier"
],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": ["error", {
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2
}]
}
}
// Или использование рекомендованного конфига:
{
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended" // Включает prettier и prettier/prettier
]
}
7.4 Рекомендуемый workflow
- IDE → форматирует при сохранении (Prettier через editor integration)
- Git hook (pre-commit) → запускает lint-staged: ESLint --fix + Prettier --write
- CI/CD → проверяет: ESLint (lint) + Prettier (format:check) + TypeScript (typecheck)
- Code Review → нет споров о стиле — всё автоматизировано
8. Автоматическое форматирование при сохранении
8.1 Настройка в VS Code
// .vscode/settings.json
{
// Форматирование при сохранении
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// Автоисправление ESLint при сохранении
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.prettier": "explicit",
"source.organizeImports": "explicit"
},
// Приоритет форматеров
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// Настройки редактора для консистентности с Prettier
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.wordWrap": "on"
}
8.2 Автоформатирование в JetBrains IDE
Настройка в WebStorm/IntelliJ IDEA:
- Settings → Languages & Frameworks → JavaScript → Prettier
- Включить "On code reformat" и "On save"
- Settings → Tools → File Watchers → добавить Prettier
- Settings → Editor → Code Style → Prettier
- Настроить "Run on Save" для автоматического форматирования
9. Расширенные техники
9.1 Shared Configurations
Для монорепозиториев и больших команд удобно создавать shared конфигурации:
// packages/eslint-config-company/index.js
// Общая конфигурация для всех проектов компании
const prettierConfig = require('./prettier-config');
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'import', 'jsx-a11y'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:jsx-a11y/recommended',
'prettier',
],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/consistent-type-imports': 'error',
'import/order': ['error', {
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
}],
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
},
};
// Использование в проекте:
// npm install --save-dev eslint-config-company
// extends: ["company"]
9.2 Кастомные Prettier плагины
// prettier-plugin-custom-sort/index.js
// Кастомный плагин для сортировки импортов
module.exports = {
parsers: {
typescript: {
...require('prettier/parser-typescript').parsers.typescript,
preprocess: (text, options) => {
// Сортировка импортов перед форматированием
const lines = text.split('\n');
const imports = [];
const rest = [];
lines.forEach(line => {
if (line.startsWith('import ')) {
imports.push(line);
} else {
rest.push(line);
}
});
// Сортировка импортов
const sortedImports = imports.sort((a, b) => {
const aIsType = a.includes('type ');
const bIsType = b.includes('type ');
if (aIsType && !bIsType) return 1;
if (!aIsType && bIsType) return -1;
return a.localeCompare(b);
});
return [...sortedImports, '', ...rest].join('\n');
},
},
},
};
9.3 Проверка в CI/CD
# .github/workflows/ci.yml — GitHub Actions
name: CI
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: TypeScript check
run: npm run typecheck
- name: ESLint
run: npm run lint
- name: Prettier check
run: npm run format:check
- name: Tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
9.4 Монорепозиторий с Turborepo
// turbo.json — Конфигурация Turborepo
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"typecheck": {
"dependsOn": ["^typecheck"],
"outputs": []
},
"lint": {
"outputs": []
},
"format": {
"outputs": []
},
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
}
}
}
// Корневой package.json
{
"scripts": {
"typecheck": "turbo run typecheck",
"lint": "turbo run lint",
"format": "turbo run format",
"format:check": "turbo run format:check",
"build": "turbo run build",
"test": "turbo run test",
"validate": "turbo run typecheck lint format:check test"
}
}
10. Практические примеры
10.1 Полная конфигурация проекта
// package.json — полная конфигурация
{
"name": "my-typescript-project",
"version": "1.0.0",
"scripts": {
"dev": "ts-node-dev --respawn src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"lint": "eslint . --ext .ts,.tsx --max-warnings=0",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",
"typecheck": "tsc --noEmit",
"validate": "npm run typecheck && npm run lint && npm run format:check",
"prepare": "husky"
},
"lint-staged": {
"src/**/*.{ts,tsx}": [
"eslint --fix --max-warnings=0",
"prettier --write"
],
"src/**/*.{json,md}": [
"prettier --write"
]
},
"devDependencies": {
"@eslint/js": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0"
}
}
10.2 Пример кода: до и после
// ДО (плохой код — много проблем):
import { useState, useEffect, useCallback } from 'react';
import { User } from '../types'
import axios from 'axios'
import { API_URL } from '../config'
import { formatDate } from '../utils'
interface Props {
userId: string;
onUpdate?: (user: User) => void
}
export const UserProfile = ({ userId, onUpdate } :Props) => {
const [user, setUser] = useState (null)
const [loading, setLoading] = useState(true)
useEffect(() => {
async function fetchUser() {
try {
const response = await axios.get(API_URL + '/users/' + userId)
setUser(response.data)
if (onUpdate) {
onUpdate(response.data)
}
} catch (error) {
console.log('Error:', error)
} finally {
setLoading(false)
}
}
fetchUser()
}, [])
if (loading) return <div>Loading...</div>
if (!user) return <div>Not found</div>
return (
<div className="profile">
<h1>{user.name}</h1>
<p>{formatDate(user.createdAt)}</p>
<p>{user.email}</p>
</div>
)
}
// ПОСЛЕ (хороший код — исправлено ESLint + Prettier):
import { useState, useEffect, useCallback } from 'react';
import axios from 'axios';
import { API_URL } from '../config';
import type { User } from '../types';
import { formatDate } from '../utils';
interface Props {
userId: string;
onUpdate?: (user: User) => void;
}
export const UserProfile = ({ userId, onUpdate }: Props) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const fetchUser = useCallback(async () => {
try {
const response = await axios.get<User>(`${API_URL}/users/${userId}`);
setUser(response.data);
onUpdate?.(response.data);
} catch (error) {
console.error('Failed to fetch user:', error);
} finally {
setLoading(false);
}
}, [userId, onUpdate]);
useEffect(() => {
void fetchUser();
}, [fetchUser]);
if (loading) {
return <div>Loading...</div>;
}
if (!user) {
return <div>Not found</div>;
}
return (
<div className="profile">
<h1>{user.name}</h1>
<p>{formatDate(user.createdAt)}</p>
<p>{user.email}</p>
</div>
);
};
11. Лучшие практики
Делайте
- Включите strict mode в tsconfig
- Используйте eslint-config-prettier
- Добавьте husky + lint-staged
- Используйте --max-warnings=0 в CI
- Настраивайте правила под проект
- Используйте shared configs в монорепо
- Регулярно обновляйте зависимости
Не делайте
- Не конфликтуйте ESLint с Prettier
- Не игнорируйте warnings без причины
- Не используйте "eslint-disable" без комментария
- Не дублируйте правила в разных конфигах
- Не забывайте обновлять .eslintignore
- Не пропускайте typecheck в CI
- Не коммитьте без проверки
11.1 Правила для команд
- Один конфиг на проект — храните конфигурацию в репозитории, не в IDE
- Не отключайте правила без PR — любое изменение правил должно проходить code review
- Используйте shared configs — для единообразия между проектами
- Автоматизируйте всё — hooks, CI, форматирование при сохранении
- Документируйте правила — если правило нестандартное, объясните почему
- Измеряйте прогресс — количество warnings/errors должно уменьшаться
12. Чек-лист настройки
Чек-лист: настройка ESLint + Prettier для TypeScript проекта
Итоги урока
В этом уроке мы подробно изучили два essential инструмента для TypeScript-разработки:
ESLint
- Статический анализ кода
- Обнаружение ошибок и проблем
- Кастомные правила и плагины
- Интеграция с TypeScript через @typescript-eslint
Prettier
- Автоматическое форматирование
- Единый стиль для команды
- Минимальная настройка
- Поддержка множества языков
Запомните: качество кода — это не роскошь, а необходимость. Инвестиции в настройку инструментов автоматизации окупаются многократно за счёт уменьшения времени на code review, исправление багов и адаптацию нового кода.
В следующем уроке мы перейдём к тестированию TypeScript кода с помощью Jest — ещё одного essential инструмента для профессиональной разработки.
ESLint и Prettier с TypeScript
10 вопросов