Files
frontend/.ai/conventions/typescript-react.md
ksv741 6a89b08ee5
Frontend verify / commits (pull_request) Successful in 31s
Frontend verify / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 10m18s
Frontend verify / commits (push) Skipped
Frontend verify / security (push) Successful in 59s
Frontend verify / verify (push) Successful in 10m53s
chore: guard presentation data boundaries
2026-10-01 20:53:23 +03:00

7.6 KiB
Raw Permalink Blame History

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<Props>; named props находятся рядом или в types-файле. Принимай props, destructure в body, без inline props typing. Только children — FC<PropsWithChildren>; дополнительные props — named type на основе PropsWithChildren<{ … }>; без props — FC.
  • Обычные exports — нижний named block; barrel делает прямой re-export. CSF сохраняет default meta и прямые story exports; configuration exceptions заданы в ESLint. Не распространяй их на application code.

Тип значений формы выводится из её schema, когда input/output совпадают. Общие props определяются у одного owner; DTO и presentation model сохраняют собственные типы при смысловом отличии. Type re-export сохраняет public imports.

Читаемость: 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. Однострочный JSX не требует обрамляющих скобок; многострочный — требует. unicorn/no-null отключён: API DTO, React refs и Mantine используют null. Сохраняй различие null/undefined из контракта вместо массовых suppressions. CSS и Markdown также держи в 80 колонках там, где это не ломает данные/ссылки.

Review проверяет то, чего синтаксис не доказывает:

Обоснованное config exception сопровождается причиной и явным ожиданием в existing lint tests, включая разрешённый и запрещённый примеры. Это позволяет настраивать профиль без случайного ослабления остальных правил.

  • Пустая строка отделяет смысловые шаги: 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; перенос не меняет 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.

Domain literals and magic values

Общее правило ownership и символических значений — 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.

@typescript-eslint/no-magic-numbers — error; fixtures/tests/stories исключены из этой дополнительной проверки. Policy получает имя и единицы; подавление допустимо для incidental value с объяснением. Обычный UI-текст не превращай в constant: его owner — i18n. 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, placement — по FSD.