За основу берём распространнённые в JS-мире правила от Airbnb:
Чтобы минимизировать количество обсуждений код-стайла в процессе код-ревью проверка части правил автоматизирована с
помощью tslint. Конфигурационный файл tslint.json
находится в корне проекта.
Со временем можно будет автоматитизировать больше правил, написав свои кастомные правила для tslint.
tslint
для особых случаев¶В некоторых случаях tslint
неправильно отрабатывает или просто необходимо сделать исключение и пренебречь правилом.
Для обхода проверок линтера можно вставлять в код специальные комментарии, которые описаны в документации:
http://palantir.github.io/tslint/usage/rule-flags/. Они позволяют отключать проверку польностью или конкретное
правило, для текущей строки, следующей строки, блока или файла целиком.
Полное отключение проверки всего файла допускается только в случаях, когда это небольшая внешняя библиотека
(не наш код), которая по каким-либо причинам используется не через npm
, а прямым копированием в utils/
.
Для своего кода допускается только отключение определённых правил для строки или для небольшого блока.
tslint
в WebStorm¶react
и redux
, исключая сторонние
библиотеки с компонентами, типа react-bootstrap
, react-native-datepicker
, которые к ядру системы никак
не отнесёшь.lib/
, util/
.common/*
,
web/common/*
, mobile/*
и т.п../
, ../
, ../../
. Не следует злоупотреблять относительными
путями, если нужно “подняться” больше, чем на 2 уровня вверх, то лучше использовать абсолютный путь.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import * as React from 'react'
import {IndexRedirect, Route} from 'react-router'
import {inject} from 'lib/ts-guice'
import {viewerQueries} from 'common/master/relayQueries'
import NoMatchPage from '../components/NoMatchPage'
import crmRoutes from './crm/routes'
import PrivateLayout from './components/PrivateLayout'
import inboxRoutes from './inbox/routes'
import profileRoutes from './profile/routes'
import scheduleRoutes from './schedule/routes'
export default {
}
|
Примечание
Стоит написать скрипт для автоматического форматирования импортов и использовать его из WebStorm как External tool.
Использование пробелов и пробельных строк для логического украшения кода приветствуется, но без злоупотреблений. Учитывайте следующие правила:
(...)
и {...}
нигде, в том числе во вставках кода в JSX, в свойствах
объекта, и в импортах.if
-блока, тела функции, объявления класса)
пробельных строк быть не должно.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Demo {
private field = "1234"
/**
* Some description
*/
method(arg: number): string {
return field + number
} // тут пробел обязателен
private anotherMethod = () => {
// ...
} // конец класса, пробела нет
}
|
Для ключей объектов, которые совпадают с названием переменной значения, предпочтительной является сокращённая запись:
1 2 3 4 5 6 7 8 9 10 11 12 | // not so good
const x = {kind: kind}
// much better
const x = {kind}
// this is ok too
const x = {
kind,
someValue: "foo",
zIndex,
}
|
Предпочтительный язык для комментариев — английский. Если выразить мысль на английском уж очень трудно, можно на русском. Действует принцип — лучше нормальный понятный комент на русском, чем никакой.
В виде док-блоков должны оформляться описания классов, функций и методов. Допустимо использовать формат док-блоков и для свойств класса, особенно, если описание занимает больше одной строки.
Док-блоки следует обязательно писать ко всем классам и функциям, предназначение которых не очевидно по их названию и/или расположению в проекте. Верно и обратное — не следует писать док-блоки к классам и функциям, если в них содержится только чуть более развёрнутое название.
Описание должно начинаться с заглавной буквы и в первом предложении отвечать на вопрос “для чего предназначен данный класс” или “что делает данная функция/метод”.
Описывать аргументы и возвращаемый результат функций следует только тогда, когда их предназначение не очевидно из названия и контекста. Типы аргументов/результата следует описывать только непосредственно синтаксисом typescript, дублировать их в док-блоке не нужно. Название аргумента от текстового описания визуально отделяем дефисом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /**
* Provides authentication routines using JST tokens
*/
class AuthService {
// bad - тривиально, отвечает на вопрос "что делать?", а не "что делает функция?"
/**
* Validate input token
*/
validateToken(token: string): boolean {
// ...
}
// good - отвечает на вопрос "что делает функция?"
/**
* Generates encryption key using special internal algorithm and salt provided by configuration.
*/
private h256Encrypt(input: string): string {
// ...
}
}
|
Для пояснения произвольных кусков кода в теле функции или класса следует использовать формат однострочных комментариев
с использованием //
, даже если комментарий растягивается на несколько строк. Формат /** ... */
должен
использоваться только для док-блоков. Следует придерживаться следующих правил:
} else {
или короткой подсказке к полю объекта/интерфейса. При этом комментарий должен быть
коротким и не может растягиваться на несколько строк.//
и началом комментария должен стоять один пробел.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class SomeClass {
// internal logic state
private st: State = {
opened: true,
wealth: 155, // USD
timeout: 10000, // ms
}
serve() {
if (x == 1 or y == 2) {
// all right, putting it all together
z = x + y
// and here is great algorithm
// see super-duper book paper to understand it
return z * z
} else { // if invalid state
// ok - начало блока, пустая строка допустима, если это нужно для лучшей читаемости
// need to throw error here
throw new Error('Ups!')
}
}
}
|
В общем случае коммит закомментированного кода не допускается, любой ненужный код должен удаляться перед коммитом.
В отдельных случаях, когда это нужно сделать очень временно, комментирование кода допускается с дополнительным
пояснительным комментарием. В таких случаях можно использовать как однострочный формат //
, так и многострочный
/* ... */
. Пояснительный комментарий должен идти непосредственно перед закоментированным блоком в однострочном
формате.
1 2 3 4 | // todo: need to be disabled for some staging tests
/* if (true) {
return 123
}*/
|
Код компонента пишется так, чтобы читая сверху вниз последовательно понимать, что он из себя представляет. Блоки в компоненте располагаются в следующем порядке:
relay
, redux
, container
и т.п.Props
и State
.componentWillMount
(если есть).this
).
Их следует выносить из кода класса и располагать после него в виде обычных функций. В том числе такие методы,
как mapStateToProps
и mapDispatchToProps
.Props
¶Интерфейс Props
необходимо описывать в несколько блоков отдельными интерфейсами.
Каждый блок необходимо начинать с обязательных свойств. Свойства-функции должны идти после свойств-скаляров.
Порядок определения блоков:
OwnProps
: собственные свойства компонента, которыми планируется управлять извне.ContainerProps
(только для контейнеров): свойства контейнера, которые будут переданы непосредственно в компонентReduxProps
: свойства, прокинутые через redux-actions
(mapDispatchToProps()
)
или взятые из глобального стейта redux
(mapStateToProps()
)ReduxFormProps
: свойства, прокинутые через reduxForm()
Props
: собственные свойства компонента, которые используются для собственных нужд
и подключаются остальными декораторамиПередаваемые функции необходимо оформлять в виде сигнатур функций и стараться избегать общих описаний типа Function
за исключением случаев, когда это вынужденная мера.
Предпочтительная форма подключения интерфейсов к компоненту: через перечисление по амперсанду при объявлении компонента:
1 2 3 | class SomeComponent extends React.Component<ContainerProps & ReduxProps, void> {
// component's implementation
}
|
Если компонент с атрибутами не помещается на одну строчку или его нужно разбить для читаемости, то допустимо использовать один из двух форматов записи:
Первое свойство пишется на одной строке с названием компонента/тега. Переносимые свойства выравниваются по первому свойству. Закрывающая угловая скобка пишется на одной строке с последним свойством:
1 2 3 4 5 6 7 | <TestComponent prop1="1234" prop2="1234"
prop3={test}/>
<TestComponent prop1="1234" prop2="1234"
prop3={test}>
<div></div>
</TestComponent>
|
Все свойства перечисляются начиная со следующей строки после названия компонента/тега с одинарным отступом. Закрывающая угловая скобка пишется на отдельной строке на уровне открывающей:
1 2 3 4 5 6 7 8 9 10 11 | <TestComponent
prop1="1234" prop2="1234"
prop3={test}
/>
<TestComponent
prop1="1234" prop2="1234"
prop3={test}
>
<div></div>
</TestComponent>
|
В обоих случаях допускается расположение нескольких свойств на строке, но это должно подчинятся какой-то очевидной логике.
Другие вариации форматирования недопустимы.
JSX
— начиная с
новой строки с одинарным отступом, закрывающая фигурная скобка — на отдельной строке на уровне открывающей.{condition &&
.{array.map(x => ... )}
.{array.filter(x => condition(x)).map(x =>
.? :
в сочетании с вложенным JSX
-кодом не применяется. Можно только с вызовом дополнительных
render-методов. Вместо этого можно использовать {condition &&
и {!condition &&
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | render() {
const isOk = this.props.prop1 && !this.props.prop2 || this.isItemsOk(items)
return (
<div>
{isOk &&
<ul className="ttt">
{items.filter(x => x.count > 0).map(item =>
<li>
<Item info={item} />
{isLast ? this.renderLastBorder() : false}
</li>
)}
</ul>
}
{!isOk &&
<div className="error">Ошибка!</div>
}
</div>
)
}
|