From 58403e414bfbe78f0f9c57e69d2e02020b9b7574 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 16 Dec 2019 23:48:42 +0700 Subject: [PATCH 1/4] Three initial rule translate by function --- README.md | 57 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8dd0965..decdafa 100644 --- a/README.md +++ b/README.md @@ -301,27 +301,31 @@ class Projector { ## Функции -### Function arguments (2 or fewer ideally) +### Аргументы функции (идеально два или меньше) -Limiting the amount of function parameters is incredibly important because it makes testing your function easier. -Having more than three leads to a combinatorial explosion where you have to test tons of different cases with each separate argument. +Ограничение колличества парамметров функции невероятно важно, потому что это делает тестирование ваших +функций проще. Наличие более 3-х аргументов приводит к комбинаторному взрыву, где вы должны протестировать +множество вариантов с каждым отдельным аргументом -One or two arguments is the ideal case, and three should be avoided if possible. Anything more than that should be consolidated. -Usually, if you have more than two arguments then your function is trying to do too much. -In cases where it's not, most of the time a higher-level object will suffice as an argument. +Один или два аргумента это идеальный случай, а три и более следует избегать, если это возможно. +Большое колличество аргументов лучше объеденять. Обычно если вы используете более двух аргументов, то ваша функция пытается +делать слишком много. В случаях когда это не так, то лучше использовать объект верхнего уровня. -Consider using object literals if you are finding yourself needing a lot of arguments. +Подумайте о том чтобы использовать объектные литералы, если вам необходимо много аргументов. -To make it obvious what properties the function expects, you can use the [destructuring](https://basarat.gitbooks.io/typescript/docs/destructuring.html) syntax. -This has a few advantages: +Для того чтобы вы знали какие параметры ожидает функция, вы можете использовать +[синтаксис деструктуризации](https://basarat.gitbooks.io/typescript/docs/destructuring.html). -1. When someone looks at the function signature, it's immediately clear what properties are being used. +Он имеет несколько преимуществ: -2. Destructuring also clones the specified primitive values of the argument object passed into the function. This can help prevent side effects. Note: objects and arrays that are destructured from the argument object are NOT cloned. +1. Когда кто-то смотрит на синатуру функции, то сразу становится понятка какие свойства она использует. -3. TypeScript warns you about unused properties, which would be impossible without destructuring. +2. Деструктуризация также клонирует примитивные значения аргумента-объекта переданного в функцию. + Это помогает избежать сайд эффекта. Заметка: объекты и массивы которые деструктурированы из аргумента-объекта не клонируются. -**Bad:** +3. TypeScript предупреждает о неиспользуемых свойствах, это было бы не возможно без деструктуризации. + +**Плохо:** ```ts function createMenu(title: string, body: string, buttonText: string, cancellable: boolean) { @@ -331,7 +335,7 @@ function createMenu(title: string, body: string, buttonText: string, cancellable createMenu('Foo', 'Bar', 'Baz', true); ``` -**Good:** +**Хорошо:** ```ts function createMenu(options: { title: string, body: string, buttonText: string, cancellable: boolean }) { @@ -346,7 +350,7 @@ createMenu({ }); ``` -You can further improve readability by using [type aliases](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases): +Вы можете еще больше повысить читаемость, если будете использовать [type aliases](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases): ```ts @@ -364,13 +368,16 @@ createMenu({ }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Functions should do one thing +### Функции должны выполнять одну задачу -This is by far the most important rule in software engineering. When functions do more than one thing, they are harder to compose, test, and reason about. When you can isolate a function to just one action, they can be refactored easily and your code will read much cleaner. If you take nothing else away from this guide other than this, you'll be ahead of many developers. +Это одно из самых важных правил в разработке ПО. Когда функции решают больше одной задачи, +их труднее объеденять, тестировать. Если вы сможете изолировать функцию так чтобы она выполняла только одну задачу, + в дальнейшем она может быть легко переработана, а ваш код будет чище. Если вы запомните только это правило из этого + руководства, то вы уже будете лучще многих разработчиков. -**Bad:** +**Плохо:** ```ts function emailClients(clients: Client[]) { @@ -383,7 +390,7 @@ function emailClients(clients: Client[]) { } ``` -**Good:** +**Хорошо:** ```ts function emailClients(clients: Client[]) { @@ -396,11 +403,11 @@ function isActiveClient(client: Client) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Function names should say what they do +### Название функций должны говорить что они делают -**Bad:** +**Плохо:** ```ts function addToDate(date: Date, month: number): Date { @@ -413,7 +420,7 @@ const date = new Date(); addToDate(date, 1); ``` -**Good:** +**Хорошо:** ```ts function addMonthToDate(date: Date, month: number): Date { @@ -424,7 +431,7 @@ const date = new Date(); addMonthToDate(date, 1); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Functions should only be one level of abstraction From e48232ae933e3d5c6ded65b8772288f0e33ab84b Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 19 Dec 2019 00:08:19 +0700 Subject: [PATCH 2/4] Three initial rule translate by function - part 2 --- README.md | 97 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index decdafa..ac45123 100644 --- a/README.md +++ b/README.md @@ -405,7 +405,7 @@ function isActiveClient(client: Client) { **[⬆ back to top](#содержание)** -### Название функций должны говорить что они делают +### Название функций должны описывать что они делают **Плохо:** @@ -433,11 +433,12 @@ addMonthToDate(date, 1); **[⬆ back to top](#содержание)** -### Functions should only be one level of abstraction +### Функции должны иметь один уровень абстракции -When you have more than one level of abstraction your function is usually doing too much. Splitting up functions leads to reusability and easier testing. +Если у вас больще одного уровня абстракции, то обычно эта функция делает слишком мноое. Разделение функций дает возможность +переиспользования и простого тестирования. -**Bad:** +**Плохо:** ```ts function parseCode(code: string) { @@ -462,7 +463,7 @@ function parseCode(code: string) { } ``` -**Good:** +**Хорошо:** ```ts const REGEXES = [ /* ... */ ]; @@ -499,22 +500,26 @@ function parse(tokens: Token[]): SyntaxTree { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Remove duplicate code +### Удаляйте дублированный код -Do your absolute best to avoid duplicate code. -Duplicate code is bad because it means that there's more than one place to alter something if you need to change some logic. +Делайте все возможное для избежания дублирования кода. +Дублирование кода плохо, тем что если вам придется править логику, её придется править в нескольких местах. -Imagine if you run a restaurant and you keep track of your inventory: all your tomatoes, onions, garlic, spices, etc. -If you have multiple lists that you keep this on, then all have to be updated when you serve a dish with tomatoes in them. -If you only have one list, there's only one place to update! +Представьте если вы открыли ресторан и ведете учет ваших продуктов: всех ваших томатов, лука, чеснока, специй и д.р.. +Если у вас закажут блюда из томатов то вам придется вносить изменения во все ваши списки. Если список будет только один +то и править нужно будет только его. -Oftentimes you have duplicate code because you have two or more slightly different things, that share a lot in common, but their differences force you to have two or more separate functions that do much of the same things. Removing duplicate code means creating an abstraction that can handle this set of different things with just one function/module/class. +Часто вы дублируете код из-за того что когда вам требуется реализовать два и более незначительно различающихся действий, +которые очень похожи, но их различия заставляют вас завести несколько функций, делающий практически одно и тоже. Удаление +дублирующихся частей кода, означает создание абстракции, обрабатывающий разную логику с помощью всего одной функции/модуля/класса. -Getting the abstraction right is critical, that's why you should follow the [SOLID](#solid) principles. Bad abstractions can be worse than duplicate code, so be careful! Having said this, if you can make a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself updating multiple places anytime you want to change one thing. +Получение абстракции имеет важное значение, поэтому вы должны следовать принципам [SOLID](#solid). Плохие абстракции могут +оказаться хуже дублирующего кода, будьте осторожны! Если вы можете сделать хорошую абстракцию делайте. Не повторяйте себя +в противном случае вы можете обнаружить себя вносящим изменения в разные места, для одной единственной логики. -**Bad:** +**Плохо:** ```ts function showDeveloperList(developers: Developer[]) { @@ -550,7 +555,7 @@ function showManagerList(managers: Manager[]) { } ``` -**Good:** +**Хорощо:** ```ts class Developer { @@ -588,13 +593,16 @@ function showEmployeeList(employee: Developer | Manager) { } ``` -You should be critical about code duplication. Sometimes there is a tradeoff between duplicated code and increased complexity by introducing unnecessary abstraction. When two implementations from two different modules look similar but live in different domains, duplication might be acceptable and preferred over extracting the common code. The extracted common code in this case introduces an indirect dependency between the two modules. +Вы должны критически относиться к дублированию кода. Иногда существует компромисс между дублированием кода и увеличением +сложности, вводя новую абстракцию. Когда две реализации из двух разных модулей выглядят одинаково, но существуют в разных +доменах, дублирование может быть приемлемым и предпочтительным вариантом, нежели объеденений в общий код. Перенос логики +в общий код, вводит косвенную зависимость между двумя модулями. -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Set default objects with Object.assign or destructuring +### Устанавливайте объекты по умолчанию с помощью Object.assign или деструктуризации -**Bad:** +**Плохо:** ```ts type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean }; @@ -611,7 +619,7 @@ function createMenu(config: MenuConfig) { createMenu({ body: 'Bar' }); ``` -**Good:** +**Хорошо:** ```ts type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean }; @@ -630,7 +638,7 @@ function createMenu(config: MenuConfig) { createMenu({ body: 'Bar' }); ``` -Alternatively, you can use destructuring with default values: +Кроме того можно использовать деструктуризацию со значениями по умолчанию: ```ts type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean }; @@ -642,17 +650,18 @@ function createMenu({ title = 'Foo', body = 'Bar', buttonText = 'Baz', cancellab createMenu({ body: 'Bar' }); ``` -To avoid any side effects and unexpected behavior by passing in explicitly the `undefined` or `null` value, you can tell the TypeScript compiler to not allow it. -See [`--strictNullChecks`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#--strictnullchecks) option in TypeScript. +Чтобы избежать каких-либо побочных эффектов и неожиданноо поведения передавая явно `undefined` или `null` вы можете сказать +компилятору TypeScript чтобы он не разрешал этого. +Смотрите [`--strictNullChecks`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#--strictnullchecks) опция для TypeScript. -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Don't use flags as function parameters +### Не используйте флаги в качестве параметров функции -Flags tell your user that this function does more than one thing. -Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. +Флаги говорят пользователю, что функция совершает более одного действия. Функция должна решать одну задачу. +Разделяйте функции, если они исполняют различные варианты кода на основе логического значения. -**Bad:** +**Плохо:** ```ts function createFile(name: string, temp: boolean) { @@ -664,7 +673,7 @@ function createFile(name: string, temp: boolean) { } ``` -**Good:** +**Хорошо:** ```ts function createTempFile(name: string) { @@ -676,20 +685,24 @@ function createFile(name: string) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Avoid Side Effects (part 1) +### Избегайте побочных эффектов (часть 1) -A function produces a side effect if it does anything other than take a value in and return another value or values. -A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a stranger. +Функция производит побочный эффект, если она совершает какое-либо действие помимо получения значения и +возврата другого значения или значений. Побочный эффект может быть записью в файл, изменением каких-то +глобальных переменных или случайным переводом всех ваших денег неизвестным лицам. + +Впрочем, побочные эффекты в программе необходимы. Пусть, как и в предыдущем примере, вам требуется запись в файл. +Опишите то, что вы хотите сделать, строго в одном месте. Не создавайте несколько функций и классов, которые пишут что-то +в конкретный файл. Создайте один сервис, который всем этим занимается. Один и только один. -Now, you do need to have side effects in a program on occasion. Like the previous example, you might need to write to a file. -What you want to do is to centralize where you are doing this. Don't have several functions and classes that write to a particular file. -Have one service that does it. One and only one. +Суть в том, чтобы избегать распространенных ошибок, таких как, например, передача состояния между объектами +без какой-либо структуры, с помощью изменяемых данных, которые может перезаписывать кто угодно, в обход +централизованного места применения побочных эффектов. Если научитесь так делать, вы станете счастливее, чем подавляющее +большинство других программистов. -The main point is to avoid common pitfalls like sharing state between objects without any structure, using mutable data types that can be written to by anything, and not centralizing where your side effects occur. If you can do this, you will be happier than the vast majority of other programmers. - -**Bad:** +**Плохо:** ```ts // Global variable referenced by following function. @@ -705,7 +718,7 @@ toBase64(); console.log(name); // expected to print 'Robert C. Martin' but instead 'Um9iZXJ0IEMuIE1hcnRpbg==' ``` -**Good:** +**Хорошо:** ```ts const name = 'Robert C. Martin'; @@ -718,7 +731,7 @@ const encodedName = toBase64(name); console.log(name); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Avoid Side Effects (part 2) From 9c3604f42e391adfa8e97968b0dc6063fb25de78 Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 19 Dec 2019 23:53:41 +0700 Subject: [PATCH 3/4] Three initial rule translate by function - part 3 --- README.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ac45123..c9faaf7 100644 --- a/README.md +++ b/README.md @@ -733,21 +733,33 @@ console.log(name); **[⬆ back to top](#содержание)** -### Avoid Side Effects (part 2) +### Избегайте побочных эффектов (Часть 2) -In JavaScript, primitives are passed by value and objects/arrays are passed by reference. In the case of objects and arrays, if your function makes a change in a shopping cart array, for example, by adding an item to purchase, then any other function that uses that `cart` array will be affected by this addition. That may be great, however it can be bad too. Let's imagine a bad situation: +В JavaScript примитивы передаются по значению, а объекты и массивы передаются по ссылке. В случае объектов или массивов, +если ваша функция вносит изменения в корзину покупок(массив), например при добавлении элемента в массив, то любая другая +функция использующаяя `корзину` массив будет зависеть от этого добавления. Это может быть как хорошо, так и плохо. Давайте +представим плохую ситуации: -The user clicks the "Purchase", button which calls a `purchase` function that spawns a network request and sends the `cart` array to the server. Because of a bad network connection, the purchase function has to keep retrying the request. Now, what if in the meantime the user accidentally clicks "Add to Cart" button on an item they don't actually want before the network request begins? If that happens and the network request begins, then that purchase function will send the accidentally added item because it has a reference to a shopping cart array that the `addItemToCart` function modified by adding an unwanted item. +Пользователь нажимает кнопку "Купить" вызывующюю функцию `purchase` которая делает сетевой запрос и отправляет `корзину` +массив на сервер. Если происходит плохое подключение к сети функция должна отправить повторный запрос. Теперь, если пользователь +случайно нажимает на кнопку "Добавить в корзину", но пока не хочет покупать товар? Если это произойдет и в этот момент начнется +запрос на сервер, то функция purchase отправит случайно добавленный элемент, так как он имеет ссылку на корзину покупок, +котора была изменена функцией `addItemToCart`. Путем добавления нежелательного элемента. -A great solution would be for the `addItemToCart` to always clone the `cart`, edit it, and return the clone. This ensures that no other functions that are holding onto a reference of the shopping cart will be affected by any changes. +Хорошим бы рещением было бы что бы функция `addItemToCart` всегда клонировала бы массив `cart` редактировала его и +возвращала клон. Это бы гарантировало, что никакие другие функции, использующие ссылку на массив корзины покупок, +не будут затронуты какими-либо изменениями. -Two caveats to mention to this approach: +Два предостережения по-поводу такого подхода: -1. There might be cases where you actually want to modify the input object, but when you adopt this programming practice you will find that those cases are pretty rare. Most things can be refactored to have no side effects! (see [pure function](https://en.wikipedia.org/wiki/Pure_function)) +1. Возможны случаи, когда вы на самом деле хотите изменить объект по ссылке, но такие случаи крайне редки. +Большинство функций могут быть объявлены без сайд эффектов! (Смотрите [pure function](https://en.wikipedia.org/wiki/Pure_function)) -2. Cloning big objects can be very expensive in terms of performance. Luckily, this isn't a big issue in practice because there are great libraries that allow this kind of programming approach to be fast and not as memory intensive as it would be for you to manually clone objects and arrays. +2.Клонирование больших объектов может быть очень нагрузочным и влиять на производительность. К счастью, это не является +большой проблемой на практике, потому что есть отличные библиотеки, которые позволяют клонировать объекты с меньшей +нагрузкой на память в отличии от клонирования вручную. -**Bad:** +**Плохо:** ```ts function addItemToCart(cart: CartItem[], item: Item): void { @@ -755,7 +767,7 @@ function addItemToCart(cart: CartItem[], item: Item): void { }; ``` -**Good:** +**Хорошо:** ```ts function addItemToCart(cart: CartItem[], item: Item): CartItem[] { @@ -767,7 +779,12 @@ function addItemToCart(cart: CartItem[], item: Item): CartItem[] { ### Don't write to global functions -Polluting globals is a bad practice in JavaScript because you could clash with another library and the user of your API would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted to extend JavaScript's native Array method to have a `diff` method that could show the difference between two arrays? You could write your new function to the `Array.prototype`, but it could clash with another library that tried to do the same thing. What if that other library was just using `diff` to find the difference between the first and last elements of an array? This is why it would be much better to just use classes and simply extend the `Array` global. +Polluting globals is a bad practice in JavaScript because you could clash with another library and the user of your API + would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted + to extend JavaScript's native Array method to have a `diff` method that could show the difference between two arrays? + You could write your new function to the `Array.prototype`, but it could clash with another library that tried to do + the same thing. What if that other library was just using `diff` to find the difference between the first and last + elements of an array? This is why it would be much better to just use classes and simply extend the `Array` global. **Bad:** From b0b525fded7eed1e335d921389340e07d01d175b Mon Sep 17 00:00:00 2001 From: Admin Date: Tue, 24 Dec 2019 23:13:37 +0700 Subject: [PATCH 4/4] Three initial rule translate by function is done --- README.md | 320 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 165 insertions(+), 155 deletions(-) diff --git a/README.md b/README.md index c9faaf7..3b5df4e 100644 --- a/README.md +++ b/README.md @@ -775,18 +775,19 @@ function addItemToCart(cart: CartItem[], item: Item): CartItem[] { }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Don't write to global functions +### Не пешите глобальные функции -Polluting globals is a bad practice in JavaScript because you could clash with another library and the user of your API - would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted - to extend JavaScript's native Array method to have a `diff` method that could show the difference between two arrays? - You could write your new function to the `Array.prototype`, but it could clash with another library that tried to do - the same thing. What if that other library was just using `diff` to find the difference between the first and last - elements of an array? This is why it would be much better to just use classes and simply extend the `Array` global. +Загрязнение глобальных переменных — плохая практика в JavaScript, так как может породить конфликты с другой библиотекой, +и пользователь вашего API не увидит ошибок, пока не получит исключение в продакшене. Давайте рассмотрим пример: +что если вы хотите расширить стандартный метод Array JavaScript, имея метод `diff` который бы вычислял различие между +двумя массивами? Вы должны были бы записать новую функцию в `Array.prototype`, но тогда она может войти в конфликт +с другой библиотекой, которая пыталась сделать то же самое. Что если другая библиотека использовала метод `diff`, +чтобы найти разницу между первым и последним элементами массива? В этом случае лучше использовать классы и просто +сделать наследование от глобального `Array`. -**Bad:** +**Плохо:** ```ts declare global { @@ -803,7 +804,7 @@ if (!Array.prototype.diff) { } ``` -**Good:** +**Хорошо:** ```ts class MyArray extends Array { @@ -814,13 +815,13 @@ class MyArray extends Array { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Favor functional programming over imperative programming +### Предпочтение функциональное программирование над императивным -Favor this style of programming when you can. +Отдавайте предпочтение этому стилю программирования, когда можете. -**Bad:** +**Плохо:** ```ts const contributions = [ @@ -846,7 +847,7 @@ for (let i = 0; i < contributions.length; i++) { } ``` -**Good:** +**Хорошо:** ```ts const contributions = [ @@ -869,11 +870,11 @@ const totalOutput = contributions .reduce((totalLines, output) => totalLines + output.linesOfCode, 0); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Encapsulate conditionals +### Инкапсулируйте условия -**Bad:** +**Плохо:** ```ts if (subscription.isTrial || account.balance > 0) { @@ -881,7 +882,7 @@ if (subscription.isTrial || account.balance > 0) { } ``` -**Good:** +**Хорошо:** ```ts function canActivateService(subscription: Subscription, account: Account) { @@ -893,11 +894,11 @@ if (canActivateService(subscription, account)) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Avoid negative conditionals +### Избегайте негативных условий -**Bad:** +**Плохо:** ```ts function isEmailNotUsed(email: string): boolean { @@ -909,7 +910,7 @@ if (isEmailNotUsed(email)) { } ``` -**Good:** +**Хорошо:** ```ts function isEmailUsed(email): boolean { @@ -921,13 +922,18 @@ if (!isEmailUsed(node)) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Avoid conditionals +### Избегайте условных операторов -This seems like an impossible task. Upon first hearing this, most people say, "how am I supposed to do anything without an `if` statement?" The answer is that you can use polymorphism to achieve the same task in many cases. The second question is usually, "well that's great but why would I want to do that?" The answer is a previous clean code concept we learned: a function should only do one thing. When you have classes and functions that have `if` statements, you are telling your user that your function does more than one thing. Remember, just do one thing. +Эта задача кажется невозможной. Большинство людей, впервые услышав это, говорят, "как я должен делать что-либо без +выражения `if`?". Ответ заключается в том, что во многих случаях для достижения тех же целей можно использовать +полиморфизм. Второй вопро обычно, "хорошо, замечательно, но почему я должен их избегать?" Ответ, предыдущая концепция +чистого кода, которую вы узнали: функция должна выполнять только одну задачу. Когда у вас есть классы и функции, +содержащие конструкцию `if`, вы говорите своему пользователю, что ваша функция выполняет больше одной задачи. +Запомните, делать только одну задачу. -**Bad:** +**Плохо:** ```ts class Airplane { @@ -953,7 +959,7 @@ class Airplane { } ``` -**Good:** +**Хорошо:** ```ts abstract class Airplane { @@ -986,15 +992,15 @@ class Cessna extends Airplane { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Avoid type checking +### Избегайте проверки типов -TypeScript is a strict syntactical superset of JavaScript and adds optional static type checking to the language. -Always prefer to specify types of variables, parameters and return values to leverage the full power of TypeScript features. -It makes refactoring more easier. +TypeScript является надмножеством синтаксиса JavaScript и добавляют дополнительные статические проверки типов для языка. +Всегда предпочитайте указывать типы переменных, параметров и возвращаемых значений, чтобы использовать +всю мощь TypeScript. Это делает рефакторинг более легким. -**Bad:** +**Плохо:** ```ts function travelToTexas(vehicle: Bicycle | Car) { @@ -1006,7 +1012,7 @@ function travelToTexas(vehicle: Bicycle | Car) { } ``` -**Good:** +**Хорошо:** ```ts type Vehicle = Bicycle | Car; @@ -1016,13 +1022,16 @@ function travelToTexas(vehicle: Vehicle) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Don't over-optimize +### Не делайте слишком много оптимизаций -Modern browsers do a lot of optimization under-the-hood at runtime. A lot of times, if you are optimizing then you are just wasting your time. There are good [resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) for seeing where optimization is lacking. Target those in the meantime, until they are fixed if they can be. +Современные браузеры производят множество оптимизаций под капотом во время исполнения кода. Оптимизируя код вручную вы +зачастую, просто тратите свое время. Есть хорошие [ресурсы](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) +для того чтобы увидеть где оптимизация отсутствует. Поглядывайте на них в свободное время, пока эти проблемы не будут +исправлены, если вообще будут, конечно. -**Bad:** +**Плохо:** ```ts // On old browsers, each iteration with uncached `list.length` would be costly @@ -1032,7 +1041,7 @@ for (let i = 0, len = list.length; i < len; i++) { } ``` -**Good:** +**Хорошо:** ```ts for (let i = 0; i < list.length; i++) { @@ -1040,14 +1049,14 @@ for (let i = 0; i < list.length; i++) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Remove dead code +### Удаляйте мертвый код -Dead code is just as bad as duplicate code. There's no reason to keep it in your codebase. -If it's not being called, get rid of it! It will still be safe in your version history if you still need it. - -**Bad:** +Мертвый код - так же плохо, как повторяющийся код. Нет никаких оснований продолжать хранить его в кодовой базе. +Если он не используется, избавьтесь от него! В случае надобности, его всегда можно найти в истории версий. + +**Плохо:** ```ts function oldRequestModule(url: string) { @@ -1062,7 +1071,7 @@ const req = requestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**Good:** +**Хорошо:** ```ts function requestModule(url: string) { @@ -1073,20 +1082,20 @@ const req = requestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** -### Use iterators and generators +### Используйте итераторы и генераторы -Use generators and iterables when working with collections of data used like a stream. -There are some good reasons: +Используйте генераторы и итераторы при работе с коллекциями данных, которые используются как поток. +Есть несколько причин для этого: -- decouples the callee from the generator implementation in a sense that callee decides how many -items to access -- lazy execution, items are streamed on demand -- built-in support for iterating items using the `for-of` syntax -- iterables allow to implement optimized iterator patterns +- отделяет вызываемый объект от реализации генератора в том смысле, что вызываемый объект решает сколько элементов + иметь для доступа +- ленивое выполнение, элементы передаются по требованию +- встроенная поддержка итерации элементов с использованием синтаксиса `for-of` +- итераторы позволяют реализовать оптимизированные паттерны итераторов -**Bad:** +**Плохо:** ```ts function fibonacci(n: number): number[] { @@ -1109,7 +1118,7 @@ function print(n: number) { print(10); ``` -**Good:** +**Хорошо:** ```ts // Generates an infinite stream of Fibonacci numbers. @@ -1135,9 +1144,10 @@ function print(n: number) { print(10); ``` -There are libraries that allow working with iterables in a similar way as with native arrays, by -chaining methods like `map`, `slice`, `forEach` etc. See [itiriri](https://www.npmjs.com/package/itiriri) for -an example of advanced manipulation with iterables (or [itiriri-async](https://www.npmjs.com/package/itiriri-async) for manipulation of async iterables). +Существуют библиотеки, которые позволяют работать с итераторами так же, как и с собственными массивами, путем +цепочка методов, таких как `map`, `slice`, `forEach` и др. Смотрите [itiriri](https://www.npmjs.com/package/itiriri) +пример продвинутой манипуляции с итераторами (или [itiriri-async](https://www.npmjs.com/package/itiriri-async) +для манипуляции с асинхронными итераторами). ```ts import itiriri from 'itiriri'; @@ -1156,7 +1166,7 @@ itiriri(fibonacci()) .forEach(fib => console.log(fib)); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Objects and Data Structures @@ -1172,7 +1182,7 @@ Using getters and setters to access data from objects that encapsulate behavior - Easy to add logging and error handling when getting and setting. - You can lazy load your object's properties, let's say getting it from a server. -**Bad:** +**Плохо:** ```ts type BankAccount = { @@ -1193,7 +1203,7 @@ if (value < 0) { account.balance = value; ``` -**Good:** +**Хорошо:** ```ts class BankAccount { @@ -1222,13 +1232,13 @@ const account = new BankAccount(); account.balance = 100; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Make objects have private/protected members TypeScript supports `public` *(default)*, `protected` and `private` accessors on class members. -**Bad:** +**Плохо:** ```ts class Circle { @@ -1248,7 +1258,7 @@ class Circle { } ``` -**Good:** +**Хорошо:** ```ts class Circle { @@ -1265,14 +1275,14 @@ class Circle { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Prefer immutability TypeScript's type system allows you to mark individual properties on an interface / class as *readonly*. This allows you to work in a functional way (unexpected mutation is bad). For more advanced scenarios there is a built-in type `Readonly` that takes a type `T` and marks all of its properties as readonly using mapped types (see [mapped types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types)). -**Bad:** +**Плохо:** ```ts interface Config { @@ -1282,7 +1292,7 @@ interface Config { } ``` -**Good:** +**Хорошо:** ```ts interface Config { @@ -1295,7 +1305,7 @@ interface Config { Case of Array, you can create a read-only array by using `ReadonlyArray`. do not allow changes such as `push()` and `fill()`, but can use features such as `concat()` and `slice()` that do not change the value. -**Bad:** +**Плохо:** ```ts const array: number[] = [ 1, 3, 5 ]; @@ -1303,7 +1313,7 @@ array = []; // error array.push(100); // array will updated ``` -**Good:** +**Хорошо:** ```ts const array: ReadonlyArray = [ 1, 3, 5 ]; @@ -1321,7 +1331,7 @@ function hoge(args: readonly string[]) { Prefer [const assertions](https://github.com/microsoft/TypeScript/wiki/What's-new-in-TypeScript#const-assertions) for literal values. -**Bad:** +**Плохо:** ```ts const config = { @@ -1341,7 +1351,7 @@ const result = readonlyData(100); result.value = 200; // value is changed ``` -**Good:** +**Хорошо:** ```ts // read-only object @@ -1363,14 +1373,14 @@ const result = readonlyData(100); result.value = 200; // error ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### type vs. interface Use type when you might need a union or intersection. Use interface when you want `extends` or `implements`. There is no strict rule however, use the one that works for you. For a more detailed explanation refer to this [answer](https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types/54101543#54101543) about the differences between `type` and `interface` in TypeScript. -**Bad:** +**Плохо:** ```ts interface EmailConfig { @@ -1392,7 +1402,7 @@ type Shape = { } ``` -**Good:** +**Хорошо:** ```ts @@ -1421,7 +1431,7 @@ class Square implements Shape { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Classes @@ -1429,7 +1439,7 @@ class Square implements Shape { The class' size is measured by its responsibility. Following the *Single Responsibility principle* a class should be small. -**Bad:** +**Плохо:** ```ts class Dashboard { @@ -1452,7 +1462,7 @@ class Dashboard { ``` -**Good:** +**Хорошо:** ```ts class Dashboard { @@ -1465,7 +1475,7 @@ class Dashboard { // ... ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### High cohesion and low coupling @@ -1476,7 +1486,7 @@ Coupling refers to how related or dependent are two classes toward each other. C Good software design has **high cohesion** and **low coupling**. -**Bad:** +**Плохо:** ```ts class UserManager { @@ -1511,7 +1521,7 @@ class UserManager { } ``` -**Good:** +**Хорошо:** ```ts class UserService { @@ -1545,7 +1555,7 @@ class UserNotifier { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Prefer composition over inheritance @@ -1559,7 +1569,7 @@ You might be wondering then, "when should I use inheritance?" It depends on your 3. You want to make global changes to derived classes by changing a base class. (Change the caloric expenditure of all animals when they move). -**Bad:** +**Плохо:** ```ts class Employee { @@ -1585,7 +1595,7 @@ class EmployeeTaxData extends Employee { } ``` -**Good:** +**Хорошо:** ```ts class Employee { @@ -1614,13 +1624,13 @@ class EmployeeTaxData { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Use method chaining This pattern is very useful and commonly used in many libraries. It allows your code to be expressive, and less verbose. For that reason, use method chaining and take a look at how clean your code will be. -**Bad:** +**Плохо:** ```ts class QueryBuilder { @@ -1657,7 +1667,7 @@ queryBuilder.orderBy('firstName', 'lastName'); const query = queryBuilder.build(); ``` -**Good:** +**Хорошо:** ```ts class QueryBuilder { @@ -1696,7 +1706,7 @@ const query = new QueryBuilder() .build(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## SOLID @@ -1704,7 +1714,7 @@ const query = new QueryBuilder() As stated in Clean Code, "There should never be more than one reason for a class to change". It's tempting to jam-pack a class with a lot of functionality, like when you can only take one suitcase on your flight. The issue with this is that your class won't be conceptually cohesive and it will give it many reasons to change. Minimizing the amount of times you need to change a class is important. It's important because if too much functionality is in one class and you modify a piece of it, it can be difficult to understand how that will affect other dependent modules in your codebase. -**Bad:** +**Плохо:** ```ts class UserSettings { @@ -1723,7 +1733,7 @@ class UserSettings { } ``` -**Good:** +**Хорошо:** ```ts class UserAuth { @@ -1751,13 +1761,13 @@ class UserSettings { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Open/Closed Principle (OCP) As stated by Bertrand Meyer, "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification." What does that mean though? This principle basically states that you should allow users to add new functionalities without changing existing code. -**Bad:** +**Плохо:** ```ts class AjaxAdapter extends Adapter { @@ -1800,7 +1810,7 @@ function makeHttpCall(url: string): Promise { } ``` -**Good:** +**Хорошо:** ```ts abstract class Adapter { @@ -1844,7 +1854,7 @@ class HttpRequester { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Liskov Substitution Principle (LSP) @@ -1852,7 +1862,7 @@ This is a scary term for a very simple concept. It's formally defined as "If S i The best explanation for this is if you have a parent class and a child class, then the parent class and child class can be used interchangeably without getting incorrect results. This might still be confusing, so let's take a look at the classic Square-Rectangle example. Mathematically, a square is a rectangle, but if you model it using the "is-a" relationship via inheritance, you quickly get into trouble. -**Bad:** +**Плохо:** ```ts class Rectangle { @@ -1913,7 +1923,7 @@ const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles); ``` -**Good:** +**Хорошо:** ```ts abstract class Shape { @@ -1961,14 +1971,14 @@ const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Interface Segregation Principle (ISP) ISP states that "Clients should not be forced to depend upon interfaces that they do not use.". This principle is very much related to the Single Responsibility Principle. What it really means is that you should always design your abstractions in a way that the clients that are using the exposed methods do not get the whole pie instead. That also include imposing the clients with the burden of implementing methods that they don’t actually need. -**Bad:** +**Плохо:** ```ts interface SmartPrinter { @@ -2006,7 +2016,7 @@ class EconomicPrinter implements SmartPrinter { } ``` -**Good:** +**Хорошо:** ```ts interface Printer { @@ -2042,7 +2052,7 @@ class EconomicPrinter implements Printer { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Dependency Inversion Principle (DIP) @@ -2056,7 +2066,7 @@ This can be hard to understand at first, but if you've worked with Angular, you' DIP is usually achieved by a using an inversion of control (IoC) container. An example of a powerful IoC container for TypeScript is [InversifyJs](https://www.npmjs.com/package/inversify) -**Bad:** +**Плохо:** ```ts import { readFile as readFileCb } from 'fs'; @@ -2091,7 +2101,7 @@ const reader = new ReportReader(); await report = await reader.read('report.xml'); ``` -**Good:** +**Хорошо:** ```ts import { readFile as readFileCb } from 'fs'; @@ -2139,7 +2149,7 @@ const reader = new ReportReader(new JsonFormatter()); await report = await reader.read('report.json'); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Testing @@ -2157,7 +2167,7 @@ There's no excuse to not write tests. There are [plenty of good JS test framewor 3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test. -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### F.I.R.S.T. rules @@ -2173,13 +2183,13 @@ Clean tests should follow the rules: - **Timely** unit tests should be written before the production code. If you write tests after the production code, you might find writing tests too hard. -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Single concept per test Tests should also follow the *Single Responsibility Principle*. Make only one assert per unit test. -**Bad:** +**Плохо:** ```ts import { assert } from 'chai'; @@ -2200,7 +2210,7 @@ describe('AwesomeDate', () => { }); ``` -**Good:** +**Хорошо:** ```ts import { assert } from 'chai'; @@ -2223,13 +2233,13 @@ describe('AwesomeDate', () => { }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### The name of the test should reveal its intention When a test fail, its name is the first indication of what may have gone wrong. -**Bad:** +**Плохо:** ```ts describe('Calendar', () => { @@ -2243,7 +2253,7 @@ describe('Calendar', () => { }); ``` -**Good:** +**Хорошо:** ```ts describe('Calendar', () => { @@ -2257,7 +2267,7 @@ describe('Calendar', () => { }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Concurrency @@ -2267,7 +2277,7 @@ Callbacks aren't clean, and they cause excessive amounts of nesting *(the callba There are utilities that transform existing functions using the callback style to a version that returns promises (for Node.js see [`util.promisify`](https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original), for general purpose see [pify](https://www.npmjs.com/package/pify), [es6-promisify](https://www.npmjs.com/package/es6-promisify)) -**Bad:** +**Плохо:** ```ts import { get } from 'request'; @@ -2298,7 +2308,7 @@ downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html' }); ``` -**Good:** +**Хорошо:** ```ts import { get } from 'request'; @@ -2328,13 +2338,13 @@ Promises supports a few helper methods that help make code more conscise: `Promise.all` is especially useful when there is a need to run tasks in parallel. `Promise.race` makes it easier to implement things like timeouts for promises. -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Async/Await are even cleaner than Promises With `async`/`await` syntax you can write code that is far cleaner and more understandable than chained promises. Within a function prefixed with `async` keyword you have a way to tell the JavaScript runtime to pause the execution of code on the `await` keyword (when used on a promise). -**Bad:** +**Плохо:** ```ts import { get } from 'request'; @@ -2352,7 +2362,7 @@ downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html' .catch(error => console.error(error)); ``` -**Good:** +**Хорошо:** ```ts import { get } from 'request'; @@ -2376,7 +2386,7 @@ try { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Error Handling @@ -2391,7 +2401,7 @@ It would be very confusing to catch a string message there and would make [debugging more painful](https://basarat.gitbooks.io/typescript/docs/types/exceptions.html#always-use-error). For the same reason you should reject promises with `Error` types. -**Bad:** +**Плохо:** ```ts function calculateTotal(items: Item[]): number { @@ -2403,7 +2413,7 @@ function get(): Promise { } ``` -**Good:** +**Хорошо:** ```ts function calculateTotal(items: Item[]): number { @@ -2443,13 +2453,13 @@ function calculateTotal(items: Item[]): Failable { For the detailed explanation of this idea refer to the [original post](https://medium.com/@dhruvrajvanshi/making-exceptions-type-safe-in-typescript-c4d200ee78e9). -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Don't ignore caught errors Doing nothing with a caught error doesn't give you the ability to ever fix or react to said error. Logging the error to the console (`console.log`) isn't much better as often times it can get lost in a sea of things printed to the console. If you wrap any bit of code in a `try/catch` it means you think an error may occur there and therefore you should have a plan, or create a code path, for when it occurs. -**Bad:** +**Плохо:** ```ts try { @@ -2467,7 +2477,7 @@ try { } ``` -**Good:** +**Хорошо:** ```ts import { logger } from './logging' @@ -2479,13 +2489,13 @@ try { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Don't ignore rejected promises For the same reason you shouldn't ignore caught errors from `try/catch`. -**Bad:** +**Плохо:** ```ts getUser() @@ -2497,7 +2507,7 @@ getUser() }); ``` -**Good:** +**Хорошо:** ```ts import { logger } from './logging' @@ -2520,7 +2530,7 @@ try { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Formatting @@ -2548,7 +2558,7 @@ Refer also to this great [TypeScript StyleGuide and Coding Conventions](https:// Capitalization tells you a lot about your variables, functions, etc. These rules are subjective, so your team can choose whatever they want. The point is, no matter what you all choose, just *be consistent*. -**Bad:** +**Плохо:** ```ts const DAYS_IN_WEEK = 7; @@ -2564,7 +2574,7 @@ type animal = { /* ... */ } type Container = { /* ... */ } ``` -**Good:** +**Хорошо:** ```ts const DAYS_IN_WEEK = 7; @@ -2583,14 +2593,14 @@ type Container = { /* ... */ } Prefer using `PascalCase` for class, interface, type and namespace names. Prefer using `camelCase` for variables, functions and class members. -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Function callers and callees should be close If a function calls another, keep those functions vertically close in the source file. Ideally, keep the caller right above the callee. We tend to read code from top-to-bottom, like a newspaper. Because of this, make your code read that way. -**Bad:** +**Плохо:** ```ts class PerformanceReview { @@ -2631,7 +2641,7 @@ const review = new PerformanceReview(employee); review.review(); ``` -**Good:** +**Хорошо:** ```ts class PerformanceReview { @@ -2672,7 +2682,7 @@ const review = new PerformanceReview(employee); review.review(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Organize imports @@ -2691,7 +2701,7 @@ With clean and easy to read import statements you can quickly see the dependenci - modules from a parent directory (i.e. `import foo from '../foo'; import qux from '../../foo/qux';`) - modules from the same or a sibling's directory (i.e. `import bar from './bar'; import baz from './bar/baz';`) -**Bad:** +**Плохо:** ```ts import { TypeDefinition } from '../types/typeDefinition'; @@ -2703,7 +2713,7 @@ import { BindingScopeEnum, Container } from 'inversify'; import 'reflect-metadata'; ``` -**Good:** +**Хорошо:** ```ts import 'reflect-metadata'; @@ -2718,7 +2728,7 @@ import { ApiCredentials, Adapters } from './common/api/authorization'; import { ConfigPlugin } from './plugins/config/configPlugin'; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Use typescript aliases @@ -2726,13 +2736,13 @@ Create prettier imports by defining the paths and baseUrl properties in the comp This will avoid long relative paths when doing imports. -**Bad:** +**Плохо:** ```ts import { UserService } from '../../../services/UserService'; ``` -**Good:** +**Хорошо:** ```ts import { UserService } from '@services/UserService'; @@ -2752,7 +2762,7 @@ import { UserService } from '@services/UserService'; ... ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Comments @@ -2765,27 +2775,27 @@ The use of a comments is an indication of failure to express without them. Code Comments are an apology, not a requirement. Good code *mostly* documents itself. -**Bad:** +**Плохо:** ```ts // Check if subscription is active. if (subscription.endDate > Date.now) { } ``` -**Good:** +**Хорошо:** ```ts const isSubscriptionActive = subscription.endDate > Date.now; if (isSubscriptionActive) { /* ... */ } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Don't leave commented out code in your codebase Version control exists for a reason. Leave old code in your history. -**Bad:** +**Плохо:** ```ts type User = { @@ -2796,7 +2806,7 @@ type User = { } ``` -**Good:** +**Хорошо:** ```ts type User = { @@ -2805,13 +2815,13 @@ type User = { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Don't have journal comments Remember, use version control! There's no need for dead code, commented code, and especially journal comments. Use `git log` to get history! -**Bad:** +**Плохо:** ```ts /** @@ -2825,7 +2835,7 @@ function combine(a: number, b: number): number { } ``` -**Good:** +**Хорошо:** ```ts function combine(a: number, b: number): number { @@ -2833,14 +2843,14 @@ function combine(a: number, b: number): number { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### Avoid positional markers They usually just add noise. Let the functions and variable names along with the proper indentation and formatting give the visual structure to your code. Most IDE support code folding feature that allows you to collapse/expand blocks of code (see Visual Studio Code [folding regions](https://code.visualstudio.com/updates/v1_17#_folding-regions)). -**Bad:** +**Плохо:** ```ts //////////////////////////////////////////////////////////////////////////////// @@ -2872,7 +2882,7 @@ class Client { }; ``` -**Good:** +**Хорошо:** ```ts class Client { @@ -2895,7 +2905,7 @@ class Client { }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ### TODO comments @@ -2905,7 +2915,7 @@ you can quickly go over the entire list of todos. Keep in mind however that a *TODO* comment is not an excuse for bad code. -**Bad:** +**Плохо:** ```ts function getActiveSubscriptions(): Promise { @@ -2914,7 +2924,7 @@ function getActiveSubscriptions(): Promise { } ``` -**Good:** +**Хорошо:** ```ts function getActiveSubscriptions(): Promise { @@ -2923,7 +2933,7 @@ function getActiveSubscriptions(): Promise { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#содержание)** ## Translations