--- title: Руководство по стилю image: /images/user-guide/notes/notes_header.png --- Header Этот документ включает правила, которые нужно соблюдать при написании кода. Цель заключается в обеспечении однородности кода, который будет легко читать и поддерживать. Для этого лучше быть немного более многословными, чем слишком краткими. Всегда держите в голове, что код читают чаще, чем пишут, особенно в проекте с открытым исходным кодом, где к нему может присоединиться кто угодно. There are a lot of rules that are not defined here, but that are automatically checked by linters. ## React ### Используйте функциональные компоненты. Всегда используйте функциональные компоненты TSX. Не используйте стандартный `import` с `const`, так как это сложнее для чтения и импорта с автозаполнением кода. ```tsx // ❌ Bad, harder to read, harder to import with code completion const MyComponent = () => { return
Hello World
; }; export default MyComponent; // ✅ Good, easy to read, easy to import with code completion export function MyComponent() { return
Hello World
; }; ``` ### Свойства Создайте тип пропсов и назовите его `(ComponentName)Props`, если нет необходимости экспортировать его. Используйте деструктуризацию пропсов. ```tsx // ❌ Bad, no type export const MyComponent = (props) =>
Hello {props.name}
; // ✅ Good, type type MyComponentProps = { name: string; }; export const MyComponent = ({ name }: MyComponentProps) =>
Hello {name}
; ``` #### Воздержитесь от использования `React.FC` или `React.FunctionComponent` для определения типов пропсов. ```tsx /* ❌ - Bad, defines the component type annotations with `FC` * - With `React.FC`, the component implicitly accepts a `children` prop * even if it's not defined in the prop type. This might not always be * desirable, especially if the component doesn't intend to render * children. */ const EmailField: React.FC<{ value: string; }> = ({ value }) => ; ``` ```tsx /* ✅ - Good, a separate type (OwnProps) is explicitly defined for the * component's props * - This method doesn't automatically include the children prop. If * you want to include it, you have to specify it in OwnProps. */ type EmailFieldProps = { value: string; }; const EmailField = ({ value }: EmailFieldProps) => ( ); ``` #### Нет разворачивания пропсов одиночной переменной в JSX-элементах. Avoid using single variable prop spreading in JSX elements, like `{...props}`. Подобная практика часто приводит к менее читаемому и сложному в поддержке коду, так как непонятно, какие пропсы принимает компонент. ```tsx /* ❌ - Bad, spreads a single variable prop into the underlying component */ const MyComponent = (props: OwnProps) => { return ; } ``` ```tsx /* ✅ - Good, Explicitly lists all props * - Enhances readability and maintainability */ const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => { return ; }; ``` Обоснование: - С первого взгляда становится яснее, какие пропсы передаются в коде, что делает его легче понятным и поддерживаемым. - Это помогает предотвратить жесткое связывание между компонентами через их пропсы. - Инструменты для анализа кода облегчают обнаружение опечаток или неиспользуемых пропсов при явном перечислении пропсов. ## JavaScript ### Используйте оператор нулевого слияния `??` ```tsx // ❌ Bad, can return 'default' even if value is 0 or '' const value = process.env.MY_VALUE || 'default'; // ✅ Good, will return 'default' only if value is null or undefined const value = process.env.MY_VALUE ?? 'default'; ``` ### Используйте опциональную цепочку `?.` ```tsx // ❌ Bad onClick && onClick(); // ✅ Good onClick?.(); ``` ## TypeScript ### Используйте `type` вместо `interface` Всегда используйте `type` вместо `interface`, так как они почти всегда пересекаются, а `type` более гибок. ```tsx // ❌ Bad interface MyInterface { name: string; } // ✅ Good type MyType = { name: string; }; ``` ### Используйте строковые литералы вместо перечислений. [Строковые литералы](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) - это основной способ обработки значений, напоминающих перечисление, в TypeScript. Они легче расширяются с помощью Pick и Omit и обеспечивают лучшее взаимодействие с разработчиком, особенно с автозаполнением кода. Вы можете увидеть, почему TypeScript рекомендует избегать перечислений [здесь](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums). ```tsx // ❌ Bad, utilizes an enum enum Color { Red = "red", Green = "green", Blue = "blue", } let color = Color.Red; ``` ```tsx // ✅ Good, utilizes a string literal let color: "red" | "green" | "blue" = "red"; ``` #### GraphQL и внутренние библиотеки Вы должны использовать перечисления, которые генерирует кодогенератор GraphQL. Также лучше использовать перечисление при использовании внутренней библиотеки, чтобы она не требовала явного указания типа строкового литерала, не связанного с внутренним API. Пример: ```TSX const { setHotkeyScopeAndMemorizePreviousScope, goBackToPreviousHotkeyScope, } = usePreviousHotkeyScope(); setHotkeyScopeAndMemorizePreviousScope( RelationPickerHotkeyScope.RelationPicker, ); ``` ## Стилизация ### Использование StyledComponents Стилизуйте компоненты с помощью [styled-components](https://emotion.sh/docs/styled). ```tsx // ❌ Bad
Hello World
``` ```tsx // ✅ Good const StyledTitle = styled.div` color: red; `; ``` Добавляйте префикс "Styled" к стилизованным компонентам, чтобы отличать их от "реальных" компонентов. ```tsx // ❌ Bad const Title = styled.div` color: red; `; ``` ```tsx // ✅ Good const StyledTitle = styled.div` color: red; `; ``` ### Темизация Использование темы для большинства стилевых компонентов является предпочтительным подходом. #### Единицы измерения Избегайте использования значений `px` или `rem` напрямую в стилизованных компонентах. Необходимые значения обычно уже определены в теме, поэтому рекомендуется использовать тему для этих целей. #### Цвета Избегайте введения новых цветов; используйте существующую палитру из темы. Если палитра не соответствует, оставьте комментарий, чтобы команда могла это исправить. ```tsx // ❌ Bad, directly specifies style values without utilizing the theme const StyledButton = styled.button` color: #333333; font-size: 1rem; font-weight: 400; margin-left: 4px; border-radius: 50px; `; ``` ```tsx // ✅ Good, utilizes the theme const StyledButton = styled.button` color: ${({ theme }) => theme.font.color.primary}; font-size: ${({ theme }) => theme.font.size.md}; font-weight: ${({ theme }) => theme.font.weight.regular}; margin-left: ${({ theme }) => theme.spacing(1)}; border-radius: ${({ theme }) => theme.border.rounded}; `; ``` ## Запрещение импорта типов Избегайте импорта типов. Чтобы поддерживать этот стандарт, ESLint проверяет и сообщает о любых нарушениях импорта типов. Это помогает сохранить согласованность и читаемость кода TypeScript. ```tsx // ❌ Bad import { type Meta, type StoryObj } from '@storybook/react'; // ❌ Bad import type { Meta, StoryObj } from '@storybook/react'; // ✅ Good import { Meta, StoryObj } from '@storybook/react'; ``` ### Почему избегать импорта типов - **Согласованность**: Избегая импорта типов и используя единый подход для импорта как типов, так и значений, кодовая база остается согласованной в стиле импорта модулей. - **Читаемость**: Избегающий импорт типов улучшает читаемость кода, делая ясным, когда вы импортируете значения или типы. Это снижает двусмысленность и облегчает понимание назначения импортируемых символов. - **Maintainability**: It enhances codebase maintainability because developers can identify and locate type-only imports when reviewing or modifying code. ### Правило ESLint An ESLint rule, `@typescript-eslint/consistent-type-imports`, enforces the no-type import standard. Это правило создаст ошибки или предупреждения для всех нарушений импорта типов. Обратите внимание, что это правило касается редких крайних случаев, когда случаются непреднамеренные импорты типов. TypeScript itself discourages this practice, as mentioned in the [TypeScript 3.8 release notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). В большинстве случаев вам не нужно использовать импорты только типов. Чтобы гарантировать соответствие вашего кода этому правилу, убедитесь, что вы запускаете ESLint как часть вашего рабочего процесса разработки.