# TypeScript и React ## Types и components - Сохраняй strict TS profile и `import type`. Для недоверенного значения используй `unknown` с narrowing; `any` не заменяет DTO/validation. Локальное исключение на нетипизированной boundary должно быть узким и объяснённым. - Строковые `enum` используются для machine codes и protocol values. `namespace` не используется; tooling сохраняет `erasableSyntaxOnly`. Для состояния с разными payload выбирай discriminated union; не добавляй необязательные поля, допускающие невозможные комбинации. - Named components — const-valued `FC`; named props находятся рядом или в types-файле. Принимай `props`, destructure в body, без inline props typing. Только children — `FC`; дополнительные props — named type на основе `PropsWithChildren<{ … }>`; без props — `FC`. - Обычные exports — нижний named block; barrel делает прямой re-export. CSF сохраняет default meta и прямые story exports; configuration exceptions заданы в [ESLint](../../eslint.config.mjs). Не распространяй их на application code. ## Читаемость: ESLint и review ESLint проверяет ограничение 80 символов (tab = 2), пустые строки между JSX siblings и вокруг многострочных declarations/control-flow/return, вынос функций и условных выражений из JSX props, отдельные Zod schema files, отсутствие SVG в приложении, optional trailing parameters и многострочные сложные union types. Generated artifacts не форматируй вручную. URL, строковые/template/regex literals и комментарии исключены из `max-len`; это исключение для содержимого, а не повод собирать длинные expressions. CSS и Markdown также держи в 80 колонках там, где это не ломает данные/ссылки. Review проверяет то, чего синтаксис не доказывает: - Пустая строка отделяет смысловые шаги: inputs, вычисления, действие, результат. - Имена derived values описывают значение, например `projectedAmount`. - Тернарник остаётся коротким выбором двух значений. Mapping, несколько веток и многострочное преобразование — отдельная функция с guard и `if/else`. - Union из object shapes оформляй несколькими строками; nullable scalar `null | string` остаётся компактным. Для повторяемого shape используй named type. - Последний необязательный аргумент — `accountId?: string`. Required аргумент перед следующим required параметром может сохранять `T | undefined`. - Helpers и действия размещай по [UI ownership](ui.md#container-vm-и-view); перенос не меняет null/zero, validation, recovery, closures и hook ordering. ## Hooks и derived state Hooks вызываются без условий и ранних выходов перед ними; custom hook нужен связному поведению, а не новому имени над одним вызовом. Derived value вычисляй при render/в VM. Пользовательское действие обрабатывай handler; `useEffect` оставляй синхронизации с внешней системой с корректным cleanup/dependencies. `useMemo`, `useCallback`, `memo` нужны измеренной стоимости или конкретному контракту стабильной identity; не добавляй их ко всем значениям/handlers. React Compiler включён в Vite, но не доказывает выигрыш и не заменяет effect correctness. Не удаляй существующую memoization без понимания её consumers. Container/VM/view и state owners — в [UI](ui.md). ## Domain literals and magic values Общее правило ownership и символических значений — [Domain values](engineering.md#domain-values). Backend vocabulary бери из generated `paths`; generated artifacts вручную не меняй. Для открытых `string` полей допускается локальный enum известных значений, используемых UI. Он не закрывает wire contract и сохраняет unknown fallback. Если generator предоставляет runtime enum, используй его. `ProblemDetailsDto.code` сейчас `string`. `ApiErrorCode` перечисляет известные UI коды, а не закрывает wire contract. `apiErrorCode` сохраняет произвольную строку; `isApiErrorCode` проверяет известное значение без cast и сохраняет fallback для неизвестного кода. Повторяющуюся обработку своди у ответственного owner; [errors](data.md#errors). `@typescript-eslint/no-magic-numbers` — error; fixtures/tests/stories исключены из этой дополнительной проверки. Policy получает имя и единицы; подавление допустимо для incidental value с объяснением. Обычный UI-текст не превращай в constant: его owner — [i18n](ui.md#строки-и-валидация). `no-restricted-syntax` запрещает inline machine codes и protocol values в production; `no-unsafe-enum-comparison` проверяет сравнения enum. Fixtures/stories сохраняют literal wire values. Новые vocabularies и props profile требуют review. ## Проверка `npm run typecheck` и `npm run lint`; behavior проверяй по [Testing](testing.md), placement — по [FSD](../architecture.md).