Скрипты
TypeScript, JavaScript
0. Введение
0.1. Термины и определения
Термин
Определение
Блок
Любая часть кода, ограниченная двумя фигурными скобками {}
, за исключением определения объекта.
Блокоподобные конструкции
Конструкции, которые по способу записи похожи на запись блоков в управляющих конструкциях или функциях/методах. Например, объекты или массивы, записанные с переносом строки.
0.2. Использованные источники
При подготовке документа были использованы следующие материалы:
a. Google JavaScript Style Guide
b. Макконнелл С. - Совершенный код. Мастер-класс, «Русская редакция», 2010. — 896 стр. : ил.
c. Ревью в Gitlab в рамках проекта workspace-web.
d. Airbnb JavaScript Style Guide
e. Принципы написания консистентного, идиоматического кода на JavaScript
g. Intellij IDEA. File | Settings | Editor | Code Style | TypeScript
h. Мартин Роберт К. - Чистый код. Создание, анализ и рефакторинг: Издательство «Питер», 2010.
1. Файлы с исходным кодом
1.1. [Не автоматизировано] Правила именования
a. Допустимые группы в имени файла: 1. Название (основная часть имени файла), которое описывает суть содержимого файла. Должно совпадать с названием класса, который содержится в файле, приведенным в соответствие с правилами именования файлов. В некоторых случаях часть имени класса должна рассматривать как содержимое группы Тип. 2. Тип, который определяет категорию, к которой относится класс. Допустимые типы: 1. enum - перечисление/псевдо-перечисление (map с константами); 2. constants - константы.
b. Файлы с тестами дополняются ещё одним суффиксом spec
.
1.2. [Не автоматизировано] Файлы с перечислениями
Использовать один файл перечисления под группу перечислений, объединенных общей идеей. Например, использовать один файл перечислений под перечисления, связанные с компонентом. При этом файл перечисления называть: <grouping>.enums.ts
1.3. [Не автоматизировано] JSDoc в начале файла
Каждый файл после секции import
'ов, если таковая имеется, должен содержать JSDoc с информацией:
a. Для чего предназначен файл/класс.
b. Автор
c. Дата создания
2. Форматирование
2.1. Скобки
2.1.1. [Автоматизировано: curly] Скобки для управляющих конструкций
Скобки необходимо писать для всех управляющих конструкций (таких как if
, else
, for
, do
, while
, а также других), даже если тело конструкции содержит всего один оператор.
2.1.2. [Автоматизировано: brace-style] Непустые блоки
Скобки для непустых блоков должны проставляться следующим образом:
Открывающая фигурная скобка должна быть на той же строке, что и управляющая конструкция.
Перед открывающей скобкой должен быть пробел.
Закрывающая фигурная скобка должна быть на отдельной строке после тела управляющей
конструкции.
Для составных управляющих конструкций следующая часть конструкции располагается
на той же строке, что и закрывающая скобка.
2.2. Отступы
2.2.1. [Автоматизировано: indent] Величина отступа
Величина отступа должна составлять 4 пробела. Не допускается использовать табуляцию.
2.2.2. [Автоматизировано: indent] Блоки и блокоподобные конструкции
Для каждого нового блока или тела блока, содержимого объектов или массивов, которые записаны в несколько строк подобно блокам, должен применяться дополнительный отступ. По завершению блока отступ должен возвращаться на предыдущий уровень.
Отступ должен применяться как для кода, так и для комментариев к коду.
2.2.3. [Автоматизировано: newline-per-chained-call] Цепочки вызовов
Все вызовы цепочки вызовов, кроме первого, должны располагаться на новой строке. При этом эти вызовы должны иметь отступ аналогичный отступу для блоков.
2.2.4. [Автоматизировано: indent, IDEA code format] Конструкция switch-case
Секции case
(default
) должны иметь такой же отступ, что и блоки. Тело секции case
(default
) должно иметь дополнительный отступ равный стандартному для блоков.
2.3. Выражения
2.3.1. [Автоматизировано: max-statements-per-line, IDEA code format] В одной строке должно быть не более одного выражения
Каждое выражение должно располагаться на своей строке. Не должно быть несколько
выражений на одной и той же строке. Выражением называется совокупность переменных,
констант, знаков операций, имен функций, скобок, которая может быть вычислена
в соответствии с синтаксисом языка программирования. Результатом вычисления
выражения является величина определенного типа. Признаком завершения выражения
является
;
.
Это же правило относится и к объявлению переменных.
2.3.2. [Автоматизировано: @typescript-eslint/semi] Выражение обязательно завершается точкой с запятой (";")
Каждое выражение должно завершаться знаком точка с запятой ";". Не следует полагаться на автоматическую простановку этого знака или умолчания для некоторых случаев типа последней строки.
2.4. Перенос выражений
Под переносом выражений следует понимать правила переноса выражения на несколько строк.
Правила могут покрывать не все случаи, однако ключевым моментом, который преследует перенос выражений - это повышение читаемости кода.
Ограничение на длину даёт преимущества, например, при ревью кода или при параллельной работе с двумя файлами. Два файла в 120 символов как раз хорошо умещаются на мониторе с разрешением 1920 px без горизонтальной прокрутки, а вот строки длиннее приводят к её появлению. При работе с одним файлом длинные строки также могут доставлять неудобства, если помимо файла открыты дополнительные панели в IDE.
2.4.1. [Не автоматизировано] В каком месте разрывать выражение
При переносе выражения в скобках открывающая скобка должна остаться на строке с именем функции, объекта, массива, закрывающая скобка - на отдельной строке. Если объект, массив, параметры функции начинают переноситься, то переносятся все. Перенос функций, переданных в качестве параметра, рассматривается в отдельном правиле.
2.4.2. [Автоматизировано: indent, IDEA code format] Отступ при переносе части выражения
Части выражения, которые перенесены на новые строки, т.е. каждая следующая строка после исходной (первой), должны иметь дополнительный отступ относительно неё. При этом должно учитываться правило переноса блоков, что может добавлять дополнительные отступы.
2.4.3. [Не автоматизировано] Параметры функции
a. Предпочтительно размещать параметры функции на той же строке, что и имя функции.
b. Если при способе записи п.1 будет превышен лимит на длину строки, то необходимо перенести все параметры по одному на каждую строку, сделав для них один горизонтальный отступ.
2.4.4. [Не автоматизировано] Тернарный оператор
a. Допустимо использовать тернарный оператор только в выражениях, в ином случае использовать if
.
b. Недопустимо переносить тернарный оператор на несколько строк. Если возникает такая необходимость, то нужно переписать тернарный оператор на оператор if
с учётом правил 4.9.5.
2.4.5. [Не автоматизировано] Перенос длинных математических выражений
Если имеем дело с длинным математическим выражением, то необходимо разбить его на несколько, выделив логические части. Математические выражения не должны переноситься на несколько строк.
2.4.6. [Не автоматизировано, но есть в IDEA code format] Перенос массивов
a. Не нужно переносить строку с массивом, если она не превышает лимит. Исключение составляет массив, в котором элементы представлены объектными литералами, количество которых превышает единицу.
b. При необходимости переноса массива объектов каждый объект начинается и заканчивается на своей строке, т.е. не имеет соседства с массивом или другим объектом.
2.4.7.[Не автоматизировано] Перенос полей объектных литералов
Поля объектного литерала переносятся, если:
Их количество более 3.
Поля в качестве значения имеют выражение, функцию (если её длина больше 20 символов и она всего 1 в строке),
объект (если он не пустой), массив (с больше, чем 1 объектов).
Длина содержимого объекта (все поля + их значения) превышает 70 символов.
Длина неперенесённого объекта превышает установленный лимит строки.
2.4.8. [Не автоматизировано] Перенос функций, переданных в качестве параметров
a. Если стрелочная функция является единственным параметром функции, но целиком не помещается в строку, то стрелочную функцию нужно записать с использованием блочной конструкции. При этом закрывающая фигурная скобка }
будет на той же строке, что и закрывающая круглая скобка )
функции, в которую передаются параметры.
b. Если параметры функции переносятся, то должны переноситься все параметры, включая стрелочную функцию. При этом закрывающая круглая скобка )
функции, в которую передаются параметры, должна быть на отдельной строке.
c. Если после стрелочной функции есть ещё какие-то параметры, то необходимо переносить все параметры.
2.4.9. [Не автоматизировано] Перенос цепочек методов
a. [Автоматизировано: newline-per-chained-call] Выражение должно переноситься по точке.
b. [Не автоматизировано] Если в цепочке вызовов есть метод, параметры которого переносятся на несколько строк, то следует эту цепочку разбивать на несколько переменных.
2.4.10. [Автоматизировано: comma-style] Перенос запятой при переносе выражений
Если выражение переносится по запятой, то запятую на новую строку переносить не нужно.
2.4.12. Что если нет правила под ситуацию, с которой столкнулись?
В этом случае избежать длинной строки и соответственно переноса строк следует путём выделения части выражения в переменную.
Если ситуация кажется довольно общей, то следует предложить правило, которым можно дополнить настоящее руководство по стилю кода.
2.5. Пробельные места
2.5.1. [Не автоматизировано] Пробельные места в вертикальном направлении
Не должно быть множественных пустых строк подряд. Допускается использование только одной для логического разделения частей кода.
Одиночная пустая строка должна быть использована в следующих случаях:
a. [Не автоматизировано] Перед конструктором и любым методом класса.
b. Внутри тела метода для логической группировки выражений.
c. [Не автоматизировано] После закрывающей фигурной скобки }
, если за ней не следует строка, на которой только закрывающая фигурная скобка }
, или эта строка не является последней.
Пустой строки не должно быть в следующих случаях:
a. [Не автоматизировано] Перед строкой с закрывающей скобкой, завершающей функцию, метод, конструктор, объект, массив, управляющую конструкцию (if-else
, for
, while
и т.д.).
b. [Не автоматизировано] В начале функции (метода). Исключение составляет только добавление пустой строки для вложенной функции, которые встречаются при написании тестов.
c. [Не автоматизировано] Между последовательно идущими объявлениями полей класса.
d. Для выделения строк с логированием, которые связаны с предыдущим выполненным действием. Строки с логирование должны быть выделены пустыми строками, если составляют специальную логическую группу, например логируют начало выполнения метода и интересующие входных параметры.
2.5.2. [Автоматизировано] Пробельные места в горизонтальном направлении
Горизонтальные пробельные места подразделяются на следующие виды:
в начале строки;
в середине строки;
в конце строки.
Пробельные места в начале строки определяются правилами для отступов.
Пробельных мест в конце строки быть не должно. Строка не должна иметь пробелов в конце.
Внутри строки допускается использование только одиночных пробелов. Исключение составляют только строковые литералы.
Одиночный пробел должен использоваться в следующих случаях:
a. [Автоматизировано: keyword-spacing] Отделение ключевого слова (типа if
, for
, catch
) от открывающей скобки ( (
, {
), которая после него на этой же строке.
b. [Автоматизировано: keyword-spacing] Отделение ключевого слова (типа else
, catch
) от закрывающей скобки (}
), которая предшествует ему на этой же строке.
c. [Автоматизировано: space-before-blocks] Перед открывающей фигурной скобкой ({
), за исключением 1. Первого аргумента при вызове функции и первого элемента в массиве: func({ a: [{ c: d }] })
. 2. В шаблонных строках: Some param: ${param}
.
d. [Автоматизировано: space-infix-ops] По обеим сторонам любого оператора с двумя аргументами a + b
и тернарного оператора condition ? statement1 : statement2
e. [Автоматизировано: comma-spacing] После запятой ,
, если она не является последним символов строки.
f. [Автоматизировано: key-spacing] После двоеточия :
и внутри фигурных скобок в объектных литералах: { a: 10 }
.
g. [Автоматизировано: spaced-comment] После знака одиночного комментария: // Комментарий
Пробел не допускается использовать:
a. До запятой ,
или точки с запятой ;
.
b. Внутри литерала массива после [
и перед ]
: [1, 20, 4]
.
2.5.3. [Автоматизировано: key-spacing] Горизонтальное выравнивание
Горизонтальное выравнивание - это добавление дополнительного количества пробелов, чтобы элементы соседних строк были выравнены в логические колонки.
Горизонтальное выравнивание не должно использоваться, т.к. его поддержка требует дополнительных усилий.
Например,
2.6. [Автоматизировано: no-extra-parens] Использование скобок для группировки
Имеет смысл добавлять скобки для группировки частей выражения в том случае, если порядок выполнения операторов неочевиден и для правильного прочтения выражения нужно обратиться к таблице приоритета операторов.
Не нужно использовать скобки:
a. Для выделения условия в тернарном операторе: condition ? statement1 : statement2
.
b. Вокруг всего выражения при использовании delete
, typeof
, instanceof
, void
, return
, throw
, case
, in
, of
, yield
.
2.7. Комментарии
Раздел не включает правила для комментариев JSDoc.
2.7.1. [Не автоматизировано] Блочные комментарии
Не нужно использовать блочные комментарии для написания комментариев к коду. Используются только для JSDoc.
2.7.2. [Не автоматизировано] Отступы
a. [Не автоматизировано] Комментарий должен иметь тот же отступ, что и код, к которому он относится.
b. [Автоматизировано: spaced-comment] Текст комментария должен быть отделен от знака комментария одиночным пробелом.
2.7.3. [Автоматизировано: line-comment-position] Не использовать комментарии в конце строки
Комментарий должен быть на отдельной строке над тем участком кода, что он поясняет. Не допускается использовать комментарий на той же строке, что и строка кода.
2.7.4. [Автоматизировано: capitalized-comments] Используемый регистр
Регистр должен соответствовать используемого регистру при написании текстовых документов, т.е. любое предложение должно начинаться с заглавной буквы, далее используются строчные.
2.7.5. [Не автоматизировано] Используемый язык
При написании комментариев необходимо использовать английский язык.
2.7.6 [Не автоматизировано] Сокращения
Недопустимо использовать сленговые сокращения в комментариях, например bcz, btw
, а использовать полные формы because, by the way
.
2.8. Классы
2.8.1. [Автоматизировано: @typescript-eslint/member-ordering] Порядок полей, методов
Поля и методы должны располагаться в следующем порядке:
a. Статические поля (static fields)
b. Поля экземпляра (instance fields)
c. Конструкторы (constructor)
d. Аксессоры (getter/setter)
e. Статические методы (static methods)
f. Методы экземпляра (instance methods)
Внутри каждой из групп порядок определяется уровнем доступа метода/свойства:
a. Публичные (public)
b. Защищенные (protected)
c. Приватные (private)
Группы полей отделять друг от друга пустой строкой.
2.8.2. [Не автоматизировано] Порядок методов одного типа и уровня доступа
a. Код должен читаться сверху вниз. Вызываемый метод должен находиться под вызывающим.
b. Если несколько методов расположены друг за другом, то сначала идёт цепочка от первого метода, затем от второго и т.д.
2.8.3. [Не автоматизировано] Группировка аксессоров (get/set)
Объявления getter/setter'ов должны идти друг за другом, если имеется и getter, и setter для одного поля/назначения. При этом getter должен предшествовать setter'у.
2.9. Спецификации
2.9.1. [Не автоматизировано] Вертикальные отступы
Спецификация по смыслу разбивается на 3 секции:
given
- (необязательная) предусловия, подготовка для выполнения проверяемого сценария;when
- вызов проверяемого метода;then
(expect
) - проверка результата выполнения метода.
Необходимо секции отделять друг от друга одним вертикальным отступом. Внутри каждой из секций дополнительные вертикальные отступы запрещены.
Исключение составляет спецификация, в которой не более 3 строк. В этом случае считается, что каждая из строк представляет собой отдельную секцию. При этом эти секции не разделяются вертикальным отступом.
Допустимо совмещать секции when
и then
, если для проверки метода достаточно одной проверки expect
.
3. Использование возможностей языка
JavaScript включает в себя множество сомнительных и даже опасных возможностей. Этот раздел определяет, какие возможности следует, а какие не следует использовать, а также дополнительные правила по их использованию.
3.1. Переменные
3.1.1. [Автоматизировано: no-var] Использовать const
и let
const
и let
a. Для объявления переменных не должно использоваться ключевое слово var
.
b. Константы объявлять ключевым словом const
.
c. По умолчанию для переменных также использовать ключевое слово const
, которое указывает что переменная далее не меняется. Использовать let
только в тех случаях, когда переменная должна изменять своё значение.
d. Если переменной присваивается объект или массив и при этом свойства объекта или элементы массива изменяются, но сама переменная более не переприсваивается, то следует использовать для объявления const
. Нужно понимать, что при этом сама переменная не меняется, т.к. в ней ссылка на объект, а значения свойств объекта меняются по этой ссылке.
3.1.2. [Не автоматизировано, но есть в IDEA code format] Объявлять по одной переменной за один раз
Должно быть не более одного объявления переменной на одной строке, не нужно объявлять несколько переменных через запятую.
3.1.3. [Не автоматизировано] Объявлять ближе к месту использования
Локальные переменные не должны объявляться в самом начале блока, которым они ограничены (например, в начале метода). Они должны объявляться максимально близко к месту их первого использования. Это упрощает чтение кода и сужает область использования переменной, зачастую включая и область видимости.
3.1.4. [Не автоматизировано] Не следует объявлять лишние переменные
Не нужно объявлять переменные в следующих случаях:
a. Если имя переменной и свойство используемого объекта один в один определяют имя переменной. В этом случае нужно просто использовать свойство объекта.
b. Строковый литерал используется только в одном месте и сам по себе является говорящим:
3.1.5. [Не автоматизировано] Одна обязанность у переменной
Если значение переменной перезаписывается, она не должна изменять свой смысл, т.е. одна переменная должна иметь одну обязанность.
3.1.6. [Не автоматизировано] Объявление переменной с присваиванием ей значения по умолчанию
a. Если нам необходимо объявить переменную, например массива какого-то типа или объекта какого-то интерфейса, то в этом случае тип переменной прописывается как в случае с обычным объявлением переменной сразу после неё, а не через утверждение типа, использую as
:
b. Если задаём значение переменной, которое однозначно определяет тип переменной, то явно тип указывать не нужно:
3.2. Массивы
3.2.1. [Автоматизировано: comma-dangle] Использование запятой для последнего элемента
За последним элементом не должно быть запятой.
3.2.2. [Не автоматизировано. Не работает правило no-array-constructor] Избегать использования конструктора Array
Array
Вместо конструктора Array
следует использовать литерал []
, если нужно создать массив с конкретными элементами. Это вызвано тем, что, во-первых, короче запись, во-вторых, меньше риск ошибиться, т.к. при передаче одного числового аргумента в Array
он создаст массив с указанным количеством элементов, которые будут иметь значение undefined
.
3.2.3. [Не автоматизировано] Оператор "spread"
a. Использовать spread-оператор вместо конструкций с Array.prototype
. Например, это актуально при работе с коллекцией аргументов функции arguments
, которая не является массивом и ей недоступны методы массива.
b. При копировании массива предпочтение отдавать spread-оператору [...arr]
, нежели методу arr.slice()
.
c. При объединении массивов предпочтение отдавать методу arr.concat(arrAnother)
, нежели spread-оператору [...arr, ...arrAnother]
.
d. После spread-оператора не должно быть пробела.
Примечание. По производительности spead-оператор примерно в 2 раза уступает
методам Array
.
3.3. Объекты
3.3.1. [Автоматизировано: comma-dangle] Использование запятой после последнего свойства
Не должно быть запятой ,
после последнего свойства, т.е. между свойством и закрывающей фигурной скобкой }
.
3.3.2. [Не автоматизировано. Не работает правило no-new-object] Использование конструктора Object
Object
Не нужно использовать конструктор Object
для создания объекта, следует использовать литерал {}
.
3.3.3. [Не автоматизировано] Кавычки для свойств объекта
Не нужно использовать кавычки при задании свойств объекта.
Называть свойства объекта следует так, чтобы не требовалось использовать кавычки для обращения к свойству объекта.
3.3.4. [Не автоматизировано] Вычисляемые названия свойств объекта
Избегать создания вычисляемых полей у объекта: { ['key' + calculateKey()]: 'value' }
.
3.3.5. [Не автоматизировано] Объявление методов
В объектных литералах не должны объявляться методы. Вместо этого должен быть написан класс, на основе которого уже могут быть созданы конкретные экземпляры с желаемыми методами.
3.3.6. [Не автоматизировано] Короткая запись свойств
Если имя переменной совпадает с именем свойства, которое нужно задать в объекте, то следует использовать короткую запись:
3.3.7. [Не автоматизировано] Использовать Map для динамически формируемых объектов
Вместо объектного литерала {}
использовать Map
, когда требуется динамически сформированная структура ключ-значение
. Map
работает быстрее.
3.5. Классы
3.5.1. [Частично автоматизировано] Конструктор
a. [Автоматизировано: no-useless-constructor] Не нужно объявлять конструктор, если он не используется.
b. [Автоматизировано: constructor-super] Для подклассов обязательно вызывать super()
в самом начале конструктора, до выполнения каких-либо других операций.
c. [Не автоматизировано] Инициализация объекта должна производиться в конструкторе, а не в отдельном методе, который выполняет всю процедуру инициализации и вызывается в конструкторе. Инициализация может быть декомпозирована на методы, если она слишком большая. При этом нужно прибегать к правилам декомпозиции методов и функций.
d. [Не автоматизировано] В конструкторе не нужно использовать return
.
e. [Не автоматизировано] В конструкторе всегда принимать объект, даже если на вход мы ожидаем только одно поле (id или title). Оборачивать это одно поле в объект. При этом параметр называть data
.
3.5.2. [Не автоматизировано] Поля
a. У приватных полей не нужно добавлять префикс _
. Исключение составляет только поле, имя которого совпадает с getter/setter'ом.
b. После создания объекта класса свойства не должны ни добавляться, ни удаляться из объекта. Это влияет на возможности оптимизации транслятора.
c. Поля должны иметь минимально возможную область видимости. Если поле может быть приватным, оно должно быть приватным. Только, если поле должно использоваться извне, тогда его стоит объявлять публичным.
3.5.3. [Не автоматизировано] Вычисляемые свойства
Запрещено использование свойств класса, имена которых являются вычисляемыми.
3.5.4. [Не автоматизировано] Методы
a. Запрещено добавлять методы при создании объекта. Все методы должны быть объявлены на уровне класса, а не добавляться непосредственно объекту при создании.
b. Методы должны иметь минимально возможную область видимости. Если метод может быть приватным, он должен быть приватным. Только, если метод должен использоваться извне, тогда его стоит объявлять публичным.
3.5.5. [Не автоматизировано] Статические методы
a. Статические методы вызывать непосредственно через имя класса, где он объявлен.
b. Не вызывать статические методы через экземпляр класса.
c. Не вызывать статические методы через производные классы.
3.5.6. [Не автоматизировано. Не работает no-extend-native] Манипуляции с prototype
prototype
Не должно быть прямых манипуляций с свойством prototype
. Необходимо использовать классы, объявленные через ключевое слово class
.
3.5.7. [Не автоматизировано] Аксессоры (getter
-ы и setter
-ы)
getter
-ы и setter
-ы)a. Не нужно делать get
-ры и set
-ры для свойств, которые можно просто использовать в качестве публичных:
b. get
-ры и set
-ры следует использовать для тех полей, что предполагают какую-то дополнительную логику поверх простого доступа к полю.
3.6. Функции
3.6.1. [Не автоматизировано] Использование классов вместо функций
Не должно быть отдельных функций.
3.6.2. [Не автоматизировано] Вложенные функции
У функции (метода) не должно быть вложенных объявлений функций, исключая анонимные функции, используемые в качестве функций обратного вызова.
3.6.3. [Не автоматизировано] Стрелочные функции
Стрелочные функции предлагают лаконичную запись и решают некоторые трудности с использованием this
внутри них. При использовании стрелочных функций () => { ... }
ключевое слово this
внутри таких функций не изменяет своего значения, т.е. имеет то же значение, что и вне функции.
a. Везде, где это возможно, нужно использовать стрелочные функции вместо обычных функций.
b. По возможности использовать наиболее короткую запись стрелочной функции без лишних переносов и фигурных скобок. В короткой записи стрелочной функции без фигурных скобок ключевое слово return
подразумевается, т.е. функция возвращает результат выражения, которое в ней записано. Допустимо использовать более полную запись функций, если это позволяет не превышать лимит на длину строки.
c. Не нужно оборачивать в скобки единственный аргумент функции.
3.6.4. [Не автоматизировано] Генераторы
Генераторы при должном их использовании позволяют писать синхронный код, если это нужно.
a. При объявлении генератора символ *
должен быть написан сразу после ключевого слова function
, если таковое имеется. Название функции/метода должно быть отделено от символа генератора *
пробелом. b. При использовании делегирующего вызова генератора символ *
должен быть сразу после yield
.
3.6.5. [Не автоматизировано] Параметры функции
a. Параметры по умолчанию должны именоваться по тем же правилам, что и обязательные параметры, не должно быть специальных префиксов или суффиксов для таких параметров.
b. Все необязательные параметры должны иметь значения по умолчанию, даже если оно undefined
.
c. Знак =
должен быть отделен от названия параметра по умолчанию и его значения одиночными пробелами.
d. В качестве значений для параметров по умолчанию использовать только литералы.
e. Отдавать предпочтение передаче объекта определенного типа, нежели большого количества параметров.
f. Использовать оператор spread ...
вместо доступа к arguments
. Параметр с оператором ...
должен быть последним в списке параметров функции.
g. Не должно быть пробела между оператором ...
и именем параметра.
h. Не следует называть параметр с оператором ...
схожим образом: varArgs
, arguments
.
3.6.6. [Не автоматизировано. не работает prefer-spread] Spread-оператор
Использовать оператор ...
вместо Function.prototype.apply
, когда требуется передать массив параметров.
3.6.7. [Не автоматизировано] Декомпозиция функции
Следует придерживаться следующих правил для выделения одной функции из другой, т.е. разбиения одной функции на несколько.
Необходимо выделить одну функцию из другой, если выполняется одно из условий:
a. Новая функция используется более чем в одном месте.
b. Исходная функция слишком большая и содержит группы из разных решаемых задач со сложной логикой.
Не следует разбивать функцию на несколько, если выполняется хотя бы одно из условий:
a. Появление новой функции только увеличивает количество кода. При этом не содержит какой-либо сложной логики и используется только в одном месте. Даже если функция может заслужить некоторое название по той операции, что она выполняет, она не заслуживает, чтобы быть отдельной.
b. Новая функция содержит фактически весь код исходной функции.
3.6.8. [Не автоматизировано] Выход из функции
a. Как правило, не рекомендуется использовать множественное return
. Необходимо использовать не более двух.
b. Допустимо использовать больше return
, если уровень вложенности структуры, внутри которой вызывается return
не превышает двух, и они сгруппированы внутри функции.
c. Использовать выход из функции в самом её начале по условию, если это позволяет исключить вложенность.
d. Не использовать выход из функции, если при этом функция не становится проще, а только увеличивается в размере.
3.6.9. [Автоматизировано] Возвращаемое значение
[Автоматизировано: no-param-reassign] a. Возвращать значение из функции с помощью return
, а не через параметры функции.
[Автоматизировано: explicit-function-return-type] b. Всегда указывать тип возвращаемого значения, даже когда возвращаемого значения нет (void). Это правило не относится к setter'ам, для которых его указывать нельзя.
3.7. Строковые литералы
3.7.1. [Автоматизировано: quotes] Тип используемых кавычек
a. Должны использоваться одиночные кавычки '
вместо двойных "
. Исключение составляют строки, когда они являются шаблонными, т.е. подразумевают вывод переменных. В этом случае используются кавычки шаблонных строк ````` .
b. Если строка включает в себя одиночную кавычку, то следует использовать шаблонную строку для её записи ```` , а не двойную кавычку
"`.
3.7.2. [Не автоматизировано] Шаблонные строки
Необходимо использовать шаблонные строки для формирования строки с переменными вместо использования конкатенации частей строки с переменными.
3.7.3. [Не автоматизировано. Не работает no-multi-str] Многострочные литералы
Не следует использовать многострочные строковые литералы ни в случае простой строки, ни в случае шаблонной строки. Нужно иметь в виду, что в случае с шаблонной строкой в строку будут включены все отступы, что имеются между символами
.
3.7.4. [Не автоматизировано] URL, пути к файлам
Если строковый литерал представляет собой URL или путь к файлу (директории), то в конце строкового литерала не нужно писать слеш, если предполагается, что этот URL или файл является базовым для какого-то подмножества и к нему будет добавляться следующая часть пути.
3.8. [Не автоматизировано] Числовые литералы
Числа могут быть определены в десятичной, шестнадцатеричной, восьмеричной и двоичной системах счисления. При использовании системы счисления отличной от десятичной необходимо в префиксе явно задавать тип системы счисления 0x
, 0o
, 0b
, недопустимо начинать число с 0
без указания системы счисления.
3.9. Управляющие конструкции
3.9.1. [Не автоматизировано] Цикл for
for
a. Предпочтение отдавать наиболее подходящему перебирающему методу (forEach
, map
, filter
, ...), если возможно их использование.
b. Если требуется использование именно цикла for
, то предпочтение отдавать циклу for ... of
.
c. Для перебора объектов использовать Object.keys(obj).forEach(...)
вместо цикла for ... in
и проверки Object.prototype.hasOwnProperty()
для исключения свойств прототипа. Метод Object.keys()
возвращает только собственные перечисляемые свойства переданного объекта.
3.9.2. [Не автоматизировано] Цикл while
while
Цикл while
использовать в тех случаях, когда заранее нет возможности определить количество итераций. В иных случаях придерживаться правил для цикла for
.
3.9.3. [Не автоматизировано] Исключения
Исключения важная часть языка, и они должны использоваться для обозначения исключительных ситуаций. Для этого в рамках приложения необходимо использоваться производный от Error
класс, который содержит необходимую для приложения информацию по ошибке.
a. [Автоматизировано: no-throw-literal] Всегда выбрасывать исключение, являющееся экземпляром класса ошибки приложения. Никогда не использовать другие типы.
b. [Автоматизировано: no-empty] Не должно быть пустых блоков обработки исключения, т.е. "проглатывания" исключений.
c. [Не автоматизировано] Объявлять и использовать переменную, инициализация которой может не выполниться, внутри секции try
, а не вне неё.
3.9.4. [Не автоматизировано] Оператор switch-case
switch-case
a. [Не автоматизировано] Не следует использовать switch
тогда, когда уместнее использовать if-else if
. Например, к такой ситуации можно отнести проверку логического условия:
b. [Автоматизировано: no-fallthrough] Каждый блок case
должен завершаться оператором break
, чтобы не было сквозного выполнения следующего блока case
. Если требуется именно такое поведение, то обязательно должен быть оставлен подробный комментарий, поясняющий необходимость такого поведения. Комментарий не требуется к группе блоков case
, для которых должно быть выполнено одно и то же действие.
c. [Не автоматизировано] Не должно быть оператора break
в секции default
.
d. [Автоматизировано: default-case] switch
обязательно должен включать секцию default
, даже если она при нормальной логике работы не должна быть задействована. В этом случае мы должны логировать ошибку.
3.9.5. [Не автоматизировано] Условия
a. [Автоматизировано: eqeqeq] Для сравнения двух операндов нужно использовать строгое равенство ===
(!==
),
не ==
(!=
), которое выполняет конвертацию типов.
b. [Автоматизировано: eigenspace/conditions] Условия должны располагаться в направлении числовой оси. Как одиночные неравенства, так и двойные без исключений. Под направление числовой оси понимается, что для положительного разрешения неравенства значение переменной должно находится правее сравниваемого значения, если неравенство типа 1 < x
, или левее, если неравенство типа x < 10
: x ∈ (1, 10) → 1 < x < 10 → 1 < x && x < 10
. Знак неравенства всегда <
, не используется знак >
.
c. [Не автоматизировано. Есть реализация с неполным покрытием (сейчас рассчет на то, что enum пишется через большую букву) в eigenspace/conditions + yoda] При использовании знака равенства при сравнении переменной и литерала (или константы) необходимо переменную писать слева от знака равенства.
d. [Частично автоматизировано: eigenspace/conditions] Не следует разбивать условия на отдельные без необходимости, когда можно записать их через ||
или &&
.
[Не автоматизировано]
[Не автоматизировано]
[Автоматизировано: eigenspace/conditions]
e. [Автоматизировано: no-extra-parens and eigenspace/conditions] Не добавлять лишних группировок и скобок в условиях, если можно этого не делать.
f. [Не автоматизировано] По возможности избегать лишней вложенности.
g. [Автоматизировано: eigenspace/conditions + no-else-return] По возможности избегать лишних ветвей else
. Это уместно, когда одно из значений является литералом, константой или представляет из себя простое выражение. Если оба значения предполагают вызов функции или сколько-нибудь сложное выражение, то предпочтение нужно отдавать полной записи условия.
h. [Автоматизировано: eigenspace/conditions] Не использовать тернарный оператор тогда, когда можно обойтись логическим оператором.
i. [Автоматизировано: no-nested-ternary] Недопустимо использовать вложенные тернарные операторы. В данном случае необходимо переписать конструкцию на использование if-elseif
, switch-case
или выбор значения из перечисления (map'ы).
3.11. Преобразование примитивов
3.11.1. [Автоматизировано: no-implicit-coercion] Использование функций-обёрток
Не использовать алгебраический оператор (например, +
), сложение с пустой строкой или двойной знак логического отрицания !
для приведения типов. Вместо этого использовать функции-обёртки для примитивов: Number
, String
, Boolean
.
3.11.2. [Не автоматизировано] Преобразование к типу Boolean
Для преобразования к типу Boolean
использовать одиночный знак отрицания !
вместо приведения типа через функцию-обёртку:
3.11.3. [Не автоматизировано] Использование функций-обёрток над примитивами вместо функций parseInt
parseInt
Отдавать предпочтение использованию Number
вместо функций parseInt
, parseFloat
.
3.11.4. [Автоматизировано: radix] Указывать систему счисления при использовании parseInt
parseInt
При необходимости использовать parseInt
обязательно указывать вторым параметром систему счисления. Это требуется, чтобы не было неожиданных результатов преобразования.
3.12. Запрещённые трюки
3.12.1. [Не автоматизировано. Не работает no-with] Оператор with
with
Не должно быть использования оператора with
. Он затрудняет понимание кода и запрещен в строгом режиме.
3.12.2. [Автоматизировано: no-eval] [Не автоматизировано. Не работает no-new-func] Динамическое построение кода
Не должно быть использования функции eval
или конструктора Function(...string)
.
3.12.4. [Автоматизировано стандартом] Нестандартизованные возможности
Не следует использовать возможности языка, которые не являются частью стандарта языка.
3.12.5. [Автоматизировано: no-new-wrappers] Обёртки над примитивами
Никогда не нужно использовать new
для обёрток над примитивами (Boolean
, Number
, String
, Symbol
). Обёртки следует вызывать как функции.
3.13. [Не автоматизировано: no-magic-numbers has problem] Магические строки и числа
"Магическими" строками и числами называются литералы, которые появляются в коде и с одной стороны зачастую не говорят о своём назначении в контексте их использования, а с другой стороны дублируются и увеличивают шансы на случайную ошибку.
3.13.1. [Не aвтоматизировано: no-magic-numbers has problem] Использовать константы вместо магических строк и чисел
Необходимо вместо магических чисел и строк использовать константы или перечисления, которые бы в полной мере описывали назначение литерала.
3.13.2. [Не автоматизировано: no-magic-numbers has import] Допустимые варианты использования литералов
Допустимо использовать без объявления константы 0
, когда он не несёт дополнительного скрытого смысла. Например, допустимо проверить на равенство нулю количество элементов в списке. Однако лучше в таких случаях сделать небольшой семантически значимый метод, который бы делал такую проверку.
3.14. Обещания (Promises)
3.14.1. [Не автоматизировано] Использовать async/await
async/await
Где это возможно использовать async/await
при вызове функций/методов, возвращающих Promise
.
3.14.2. [Не автоматизировано] Не допускать вложенности Promise
Promise
При использовании Promise
'ов не допускать вложенности одного в другое. Вместо этого использовать возможности цепочки promise'ов.
3.14.3. [Не автоматизировано] Использовать наиболее лаконичную запись
Использовать более короткую запись, если это возможно.
3.15. async/await
3.15.1. [Не автоматизировано] Обработка ошибок
Обработка ошибок реализуется через обычный try-catch
:
3.16. Экспорт/импорт
3.16.1. [Не автоматизировано: не работает eslint-plugin-import] Экспорт классов
Файл с классом должен содержать только один класс и его экспортировать. Исключением является экспорт интерфейсов, дополняющих описание класса.
Не допускается использования экспорта по умолчанию, поскольку в этом случае возникают проблемы с переименованием класса, а также проблемы во время оптимизации сборки при работе с angular.
3.17. Комментарии
3.17.1. [Не автоматизировано] Самоописываемый код
В идеале код должен быть написан так, что он является самоописываемым, т.е. и переменные, и методы, и классы говорят сами за себя, создавая полноценные абстракции, не требуя дополнительных комментариев, которые бы поясняли их назначение.
3.17.2. [Не автоматизировано] Псевдокод и комментарии
В комментарии может быть преобразован псевдокод или шаги алгоритма, если ими был набросан ход реализации метода.
3.17.3. [Не автоматизировано] Описание хитростей
В ситуациях, когда по какой-либо причине присутствуют какие-либо "хитрости" или сложности при реализации, но, опять же, по каким-то причинам нет возможности в настоящее время их преодолеть, необходимо подобные участки кода обильно снабдить комментариями, которые поясняют хитрости и причины, по которым не удалось сделать иначе.
3.18. Map
3.18.1. [Не автоматизировано] Создание Map на основе массива
При создании Map
на основе данных массива следует использовать возможность передавать в конструктор Map
итерируемый объект, содержащий элемент [key, value]
.
3.19. setTimeout
3.19.1. [Не автоматизировано] Указание задержки срабатывания
Не нужно указывать нулевую задержку для setTimeout
:
4. Правила именования
4.1. [Не автоматизировано] Общие правила для всех идентификаторов
a. Используются только буквы латинского алфавита [a-zA-Z]
, цифры [0-9]
.
b. Знак _
используется для объявления приватных полей и методов, если нет возможности объявить поле/метод приватным за счёт языковых конструкций.
c. В особых случаях, связанных со спецификой применяемого framework'а, допускается использование других символов (например, $
для angular).
d. Необходимо давать максимально говорящие, понятные имена. Делать выбор в пользу более длинного названия, нежели в пользу менее понятного. В то же время необходимо, чтобы названия по возможности состояли из наименьшего количества слов (в идеале из одного), и их смысл был ясен, исходя из контекста. Идеальное название - короткое, лаконичное и в то же время полностью раскрывающее назначение.
e. Не использовать аббревиатуры, которые не являются общепринятыми.
f. Не использовать сокращений, которые не являются общепринятыми.
g. Использовать слова count
и number of
для названия переменных, в которых содержится некоторое количество. В случаях, когда может быть двусмысленность при использовании count
, отдавать предпочтение варианту с number of
h. Не использовать более двух идущих подряд существительных. Добавлять в этом случае какие-то предлоги-связки, например of
.
i. Не использовать в качестве названий слова являющиеся служебными для языка, например get
, set
, undefined
и т.д.
j. Имена переменных должны состоять из слов, имеющих определенное смысловое значение в английском языке. Не допускается использовать транслитерацию.
k. Не следует использовать венгерскую нотацию, т.е. не нужно кодировать тип в имени переменной.
4.2. Правила для идентификаторов каждого типа
4.2.1. [Не автоматизировано] Имена классов
a. [Автоматизировано: class-name] Имена классов должны соответствовать UpperCamelCase
. Это касается как экспортируемых, так и приватных классов.
b. [Не автоматизировано] Имя должно состоять из существительных и прилагательных.
c. [Не автоматизировано] Должно использоваться единственное число.
4.2.2. [Не автоматизировано] Имена методов
Имена методов должны соответствовать lowerCamelCase
.
Имя, как правило, должно включать глагол, указывающий на действие, которое выполняет метод. При этом используемый глагол должен максимально отражать суть того, что делает метод. Например, не должно быть метода getSomething
, который ничего не возвращает.
4.2.3. [Не автоматизировано] Имена перечислений
Имя перечисления должно соответствовать UpperCamelCase
, как и для классов. Не использовать для перечисления на конце слово Type
. Имеет смысл использовать его только тогда, когда перечисление является перечислением каких-то типов.
Имя должно состоять из одиночного существительного или существительных и прилагательных.
Элементы перечисления именуются также, как и константы.
4.2.4. [Не автоматизировано] Имена констант
a. [Не автоматизировано] Имена констант должны соответствовать UPPER_SNAKE_CASE
: все буквы в верхнем регистре, слова разделены нижнем подчёркиванием.
Объявление константы:
b. [Не автоматизировано] Константа, объявленная внутри функции/метода, должна именоваться также, как переменная.
4.2.5. [Не автоматизировано] Имена полей класса
Имена изменяемых полей класса (статичных или принадлежащих экземпляру) должны соответствовать lowerCamelCase
.
Имена должны состоять из существительных и прилагательных.
4.2.6. [Не автоматизировано] Имена параметров
Имена параметров должны соответствовать lowerCamelCase
. Такое же правило применяется к параметру, который принимает конструктор в качестве значения.
4.2.7. [Не автоматизировано] Имена локальных переменных
Имена переменных должны соответствовать lowerCamelCase
. Такое же правило применяется к переменной, которая содержит конструктор в качестве значения.
Имена переменных должны состоять из прилагательных и существительных.
4.2.8. [Не автоматизировано] Имена параметров в setter'ах
Для всех setter'ов параметр называть value
:
4.3. [Не автоматизировано] Названия в верхнем регистре
Если фраза, которая должна стать названием идентификатора, содержит общепринятые аббревиатуры и названия типа IPv6, iOS, то следует поступать так:
a. Все слова фразы привести к нижнему регистру, убрав все спецсимволы.
b. Привести первую букву каждого слова к верхнему регистру, если требуется UpperCamelCase
, иначе для всех, кроме первого слова, если требуется lowerCamelCase
.
c. Объединить полученные слова в имя идентификатора.
4.4. Используемые для именования слова и сочетания
Слово
Парное
Назначение
enable
disable
Активация/деактивация какого-то элемента. В результате этого действия элемент (сущность) становится доступной (недоступной).
enabled
disabled
Элемент активен/неактивен, т.е. допускает или не допускает взаимодействие с ним.
checked
unchecked
Элемент отмечен/ не отмечен (выбран/не выбран). Наиболее соответствует ситуации, когда элемент выбирается в списке с флажками (checkbox'ами).
state
-
Используется для именования состояния элемента.
filter
-
Используется для именования фильтра.
factory
-
Фабрика каких-то объектов.
4.5. [Не автоматизировано] Поля логического типа
a. [Не автоматизировано] Поля, которые содержат признак того, имеется ли какое-то свойство или нет (поле-флаг), обозначаются как has<существительное>
. Например, hasBorder
.
b. [Не автоматизировано] Поля, которые определяют возможность использовать какой-то функционал, обозначаются как is<возможность>Enabled
. Например, isLoggingEnabled
.
c. [Не автоматизировано] Любой однозначный логический тип определяется причастием или отглагольным прилагательным. Например: hidden
, busy
, disabled
и т.д.
4.6. Спецификации
4.6.1. [Не автоматизировано] Именование переменных под объекты заглушек
Имя переменной-конструктора для заглушки должно быть в виде Mock<имя класса, на экземляр которого делается заглушка>
. Переменной, являющейся экземпляром класса-заглушки, необходимо давать имя в следующем формате: mock<имя класса, на экземляр которого делается заглушка>
.
5. TSDoc/JSDoc
TSDoc/JSDoc должен использоваться для публичного интерфейса классов и модулей, описывая назначение, входные, выходные параметры, исключения и возвращаемое значение.
5.1. [Не автоматизировано: jsdoc-format] Общая форма
В общем виде JSDoc блок выглядит следующим образом:
Комментарий включает:
a. Базовое описание.
b. Секцию с параметрами, возвращаемым значением.
5.2. [Не автоматизировано] Общие правила
a. Базовое описание отделяется от секции с параметрами пустой строкой.
b. Тело документации не должно начинаться и заканчиваться пустой строкой.
c. Длина каждой строки JSDoc не должна превышать установленное ограничение на длину строки.
d. Комментарий пишется в Markdown. Например, ненумерованный список задаётся следующим образом:
e. Перенос строк для секции параметров выполняется с дополнительным отступом.
6. Политики
6.1. [Автоматизировано: code coverage] Покрытие кода спецификациями
a. При внесении изменений должны быть спецификации (тесты), которые описывают требования, которые эти изменения реализуют, и проверяются их.
b. Когда пишем спецификации необходимо обращать внимание на процент покрытия кода спецификациями (code coverage). Необходимо доводить покрытие до 100%. Меньше 100% допускается только, если в классе есть методы, которые проверять не нужно (фиктивные, искусственные методы, которые пробрасывают функциональность с одного уровня приложения на другой).
7. Принципы
7.1. Логирование
7.1.1. Что логируем
TODO
7.1.2. Как логируем
TODO
7.1.3. Где логируем
TODO
a. Не нужно логировать на каждом уровне, что можно залогировать на общем.
7.2. Проверки в коде
TODO
7.3. [Автоматизировано: no-unused-vars] Неиспользуемые переменные, классы, методы
В коде не должно быть неиспользованных констант, методов, классов, переменных. Неиспользуемые выражения и конструкции нужно удалять.
8. TODO
8.1. Общее
a. Языки:
css
html
8.2. [Не автоматизировано] Принципы
a. Сервисы должны иметь конкретные методы, которые скрывают детали. Какие-то общие методы должны быть приватными.
b. Логирование
c. Количество и суть проверок.
d. Обработка входного параметра конструктора, функции.
e. asserts (утверждения), правила их использования, защитное программирование.
f. Минимально необходимое решение, удовлетворяющее требованиям
Last updated