Кодстайл фронтенда Эвотора ориентируется в первую очередь на официальный кодстайл Angular, поэтому настоятельно рекомендуем внимательно ознакомиться с этим документом.
Также рекомендуется ознакомиться с кратким списком Do’s and Don’ts от Typescript.
Ниже описаны ключевые моменты и особенности, которые не покрыты (или упомянуты вскользь) в основном кодстайле Angular.
Под сущностью в данном руководстве подразумевается:
| Регистр | Использование |
|---|---|
kebab-case или dashed-case |
названия файлов / селектор компонента |
PascalCase или UpperCamelCase |
классы / интерфейсы / типы / enum / декораторы / параметры типов |
lowerCamelCase или просто camelCase |
локальные переменные / параметры функций / функции / методы / свойства / селектор директивы |
UPPER_SNAKE_CASE или SCREAMING_SNAKE_CASE |
глобальные константы |
lower_snake_case или просто snake_case |
не используется в TypeScript |
Аббревиатуры в названиях сущностей НУЖНО рассматривать как целые слова (акронимы), поэтому не следует выделять их в названиях сущностей заглавными буквами (кроме случаев, когда это требуется внешним API):
Поэтому корректно писать
getFeedHtml, а не getFeedHTMLloadApiUrl, а не loadAPIURLis или has.Хорошая статья на эту тему.
$$ для названий свойств класса и переменных типа Observable.__. В том числе, для обозначения приватных свойств класса.Единственный ДОПУСТИМЫЙ кейс использования префикса — инкапсуляция значения через публичный геттер / сеттер:
class AuthTokenStore {
private _token: string;
get token(): string {
return this._token;
}
}
При необходимости создания псевдонимов внутри класса для сущностей вне этого класса СЛЕДУЕТ наследовать регистр и название этой сущности
const LIMIT = 10;
class NewsListModel {
readonly LIMIT = LIMIT;
}
I: INewsListInterface: NewsListInterfaceЗамечание: при создании интерфейса СЛЕДУЕТ подумать, для чего этот интерфейс вообще создаётся, и эту особенность нужно отразить в названии. При этом необходимо помнить про принцип разделения интерфейсов (ISP) — он поможет избежать перегруженных интерфейсов, которые не несут ничего, кроме отчаяния.
Для работы с API СЛЕДУЕТ использовать суффиксы Response / Request:
NewsItemResponse — для описания объекта ответа сервераNewsItemRequest — для описания объекта тела запроса на серверЕсли метод или функция ничего не возвращают, НЕОБХОДИМО указать тип void:
class NewsCardComponent implements OnInit {
// bad
ngOnInit() {
}
// good
ngOnInit(): void {
}
}
kebab-case. Другой регистр НЕ ДОПУСКАЕТСЯ.<feature>.<type>.<extension>.<type> НЕОБХОДИМО использовать для следующих типов сущностей:
.module.component.directive.service.pipe.decorator.interceptor.validator.guard.model.collection<type> НЕ ДОПУСКАЕТСЯ: если тип сущности не указан выше, то суффикс не применяется.kebab-case.classes — для абстрактных и других вспомогательных классов (кроме классов моделей)components — компонентыdirectives — директивыconstants — глобальные константыguards — гарды роутераinterceptors — интерсепторыinterfaces — интерфейсыenums — перечисленияtypes — типыpages — ЖЕЛАТЕЛЬНО выделить отдельную папку для компонентов страницpipes — пайпыservices — сервисыmodels — классы моделей и коллекций предметной областиmodules — модулиvalidators — валидаторы формutils — утилиты (классы со статичными методами)filename: product-type.enum.ts
export enum AppProductType {
}
Проблемы:
App, в названии файла это не отражено.enumpublic ДОПУСКАЕТСЯ использовать только для парамеров конструктора класса.public для свойств и методов класса избыточен — в Typescript всё публично по умолчанию.readonlyreadonly входящие и исходящие свойства компонентов (с @Input и @Output декораторами).readonly псевдонимы внешних сущностей:
const LIMIT = 10;
class NewsListModel {
readonly LIMIT = LIMIT;
}
Компоненты, реализующие шаблон-контейнер раздела приложения.
-page в блоке названия файла <feature>.pages.Пример структуры:
- pages
- home-page
- home-page.component.ts
- home-page.component.html
- home-page.component.scss
a += ba -= ba *= ba /= ba **= ba ??= ba ||= bif:
const t = 1;
// bad
(t > 0) && foo(t);
// good
if (t > 0) {
foo(t);
}
!! (двойное отрицание)ДОПУСКАЕТСЯ приведение к булеву типу при помощи двойного отрицания:
function hasValue(value: string): boolean {
return !!value;
}
|| (логическое ИЛИ)ДОПУСКАЕТСЯ использование логического ИЛИ в операциях присваивания:
const valueWithFallback = value || 'fallback';
?? (нулевое слияние)ДОПУСКАЕТСЯ использование оператора нулевого слияния:
const valueWithFallback = value ?? 'fallback';
// bad
const bad1 = 11.1 | 0;
const bad2 = "11.1" | 0;
const bad3 = ~~"11.1";
// good
const good1 = parseInt(11.1, 10);
+ // bad
const badNum = +str;
// good
const goodNum = Number(str);
// good (if you know what you are doing)
const goodNum2 = parseInt(str, 10);
? :
class FormComponent {
// bad
setDisabledState(isDisabled: boolean): void {
isDisabled ? this.formControl.disable() : this.formControl.enable();
}
// good
setDisabledState(isDisabled: boolean): void {
const action = isDisabled ? 'disable' : 'enable';
this.formControl[action]();
}
// also good
setDisabledState(isDisabled: boolean): void {
this.formControl[isDisabled ? 'disable' : 'enable']();
}
}
const result = value
? Math.PI > 4
: 'nope';