Скрипты
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. [Не автоматизировано] Объявление методов
В объектных литералах не должны объявляться методы. Вместо этого должен быть написан класс, на основе которого уже могут быть созданы конкретные экземпляры с желаемыми методами.