mirror of
https://github.com/labs42io/clean-code-typescript.git
synced 2025-04-18 15:13:34 +00:00
Translate updated
This commit is contained in:
parent
7f334c66c5
commit
211df86d6d
1 changed files with 49 additions and 77 deletions
126
README.md
126
README.md
|
@ -166,7 +166,7 @@ for (const [id, user] of users) {
|
|||
|
||||
**[⬆ Вернуться в начало](#содержание)**
|
||||
|
||||
### Избегайте ментальных связей
|
||||
### Избегайте мысленного связывания
|
||||
|
||||
Явное лучше, чем неявное.
|
||||
*Ясность - это король.*
|
||||
|
@ -248,7 +248,7 @@ function loadPages(count: number = 10) {
|
|||
|
||||
### Используйте enum дл документирования
|
||||
|
||||
Enam'ы могут помочь документированию вашего кода. Например когда мы обеспокоены, что наши переменные
|
||||
Enam'ы могут помочь документированию вашего кода. Например когда мы обеспокоены тем, что наши переменные
|
||||
отличаются от значений.
|
||||
|
||||
**Плохо:**
|
||||
|
@ -998,7 +998,7 @@ class Cessna extends Airplane {
|
|||
|
||||
TypeScript является надмножеством синтаксиса JavaScript и добавляют дополнительные статические проверки типов для языка.
|
||||
Всегда предпочитайте указывать типы переменных, параметров и возвращаемых значений, чтобы использовать
|
||||
всю мощь TypeScript. Это делает рефакторинг более легким.
|
||||
всю мощь TypeScript. Это делает будущий рефакторинг более легким.
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
@ -1279,7 +1279,7 @@ class Circle {
|
|||
|
||||
**[⬆ back to top](#содержание)**
|
||||
|
||||
### Предпочитайте иммутабельность
|
||||
### Используйте иммутабельность
|
||||
|
||||
Система типов в TypeScript позволяет помечать отдельные свойства интерфейса/класса как *readonly поля (только для чтения)*.
|
||||
Это позволяет вам работать функционально (неожиданная мутация это плохо).
|
||||
|
@ -1306,8 +1306,7 @@ interface Config {
|
|||
}
|
||||
```
|
||||
|
||||
В случае массива вы можете создать массив только для чтения, используя `ReadonlyArray<T>`.
|
||||
не делайте изменений с использованием `push()` и `fill()`, но можно использовать `concat()` и `slice()` они не меняют значения.
|
||||
В случае массива вы можете создать массив только для чтения, используя `ReadonlyArray<T>`. который не позволяет делать изменения с использованием `push()` и `fill()`, но можно использовать `concat()` и `slice()` они не меняют значения.
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
@ -1341,47 +1340,47 @@ function hoge(args: readonly string[]) {
|
|||
const config = {
|
||||
hello: 'world'
|
||||
};
|
||||
config.hello = 'world'; // value is changed
|
||||
config.hello = 'world'; // значение изменено
|
||||
|
||||
const array = [ 1, 3, 5 ];
|
||||
array[0] = 10; // value is changed
|
||||
array[0] = 10; // значение изменено
|
||||
|
||||
// writable objects is returned
|
||||
// записываемые объекты возвращаются
|
||||
function readonlyData(value: number) {
|
||||
return { value };
|
||||
}
|
||||
|
||||
const result = readonlyData(100);
|
||||
result.value = 200; // value is changed
|
||||
result.value = 200; // значение изменено
|
||||
```
|
||||
|
||||
**Хорошо:**
|
||||
|
||||
```ts
|
||||
// read-only object
|
||||
// объект только для чтения
|
||||
const config = {
|
||||
hello: 'world'
|
||||
} as const;
|
||||
config.hello = 'world'; // error
|
||||
config.hello = 'world'; // ошибка
|
||||
|
||||
// read-only array
|
||||
// массив только для чтения
|
||||
const array = [ 1, 3, 5 ] as const;
|
||||
array[0] = 10; // error
|
||||
array[0] = 10; // ошибка
|
||||
|
||||
// You can return read-only objects
|
||||
// Вы можете вернуть объект только для чтения
|
||||
function readonlyData(value: number) {
|
||||
return { value } as const;
|
||||
}
|
||||
|
||||
const result = readonlyData(100);
|
||||
result.value = 200; // error
|
||||
result.value = 200; // ошибка
|
||||
```
|
||||
|
||||
**[⬆ back to top](#содержание)**
|
||||
|
||||
### Типы vs. интерфейсы
|
||||
|
||||
Используйте типы, когда вам может понадобиться объединение или пересечение. Используйте интерфейс, когда хотите `extends`
|
||||
Используйте типы, когда вам может понадобиться объединение или пересечение. Используйте интерфейс, когда хотите использовать `extends`
|
||||
или `implements`. Однако строгого правила не существует, используйте то, что работает у вас.
|
||||
Для более подробного объяснения посмотрите это [ответы](https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types/54101543#54101543)
|
||||
о различиях между `type` and `interface` в TypeScript.
|
||||
|
@ -1465,7 +1464,6 @@ class Dashboard {
|
|||
getVersion(): string { /* ... */ }
|
||||
// ...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Хорошо:**
|
||||
|
@ -1477,7 +1475,7 @@ class Dashboard {
|
|||
getVersion(): string { /* ... */ }
|
||||
}
|
||||
|
||||
// split the responsibilities by moving the remaining methods to other classes
|
||||
// разделить обязанности, переместив оставшиеся методы в другие классы
|
||||
// ...
|
||||
```
|
||||
|
||||
|
@ -1487,19 +1485,19 @@ class Dashboard {
|
|||
|
||||
Сплоченность определяет степень, в которой члены класса связаны друг с другом. В идеале все поля в классе должны
|
||||
использоваться каждым методом. Мы говорим, что класс *максимально связный*. На практике это, однако, не всегда возможно
|
||||
и даже не желательно. Однако вы должны добиваться, того чтобы сплоченность была высокой.
|
||||
и даже нежелательно. Однако вы должны добиваться, того чтобы сплоченность была высокой.
|
||||
|
||||
Соединение относится и к тому, как связаны или зависимы два класса друг от друга. Классы считаются слабосвязанными если
|
||||
Связанность относится и к тому, как связаны или зависимы два класса друг от друга. Классы считаются слабосвязанными если
|
||||
изменения в одном из них не влияют на другой.
|
||||
|
||||
**Плохо:**
|
||||
|
||||
```ts
|
||||
class UserManager {
|
||||
// Bad: each private variable is used by one or another group of methods.
|
||||
// It makes clear evidence that the class is holding more than a single responsibility.
|
||||
// If I need only to create the service to get the transactions for a user,
|
||||
// I'm still forced to pass and instance of `emailSender`.
|
||||
// Плохо: каждая закрытая переменная используется той или иной группой методов.
|
||||
// Это ясно показывает, что класс несет больше, чем одну ответственность
|
||||
// Если мне нужно только создать сервис, чтобы получить транзакции для пользователя,
|
||||
// Я все еще вынужден передавать экземпляр `emailSender`.
|
||||
constructor(
|
||||
private readonly db: Database,
|
||||
private readonly emailSender: EmailSender) {
|
||||
|
@ -1565,7 +1563,7 @@ class UserNotifier {
|
|||
|
||||
### Предпочитайте композицию наследованию
|
||||
|
||||
Как сказано в [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) от банды черытех в должны
|
||||
Как сказано в [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) от банды черытех вы должны
|
||||
*Предпочитать композицию наследованию* где можете. Есть много веских причин использовать наследование и много хороших
|
||||
причин использовать композицию. Суть этого принципа в том, что если ваш ум инстинктивно идет на наследование,
|
||||
попробуйте подумать, может ли композиция лучше смоделировать вашу проблему. В некоторых случаях может.
|
||||
|
@ -1591,7 +1589,7 @@ class Employee {
|
|||
// ...
|
||||
}
|
||||
|
||||
// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
|
||||
// Плохо, потому что Employees "имеют" налоговые данные. EmployeeTaxData не является типом Employee
|
||||
class EmployeeTaxData extends Employee {
|
||||
constructor(
|
||||
name: string,
|
||||
|
@ -1636,7 +1634,7 @@ class EmployeeTaxData {
|
|||
|
||||
**[⬆ back to top](#содержание)**
|
||||
|
||||
### Используйте цепочки методов
|
||||
### Используйте цепочки вызовов
|
||||
|
||||
Этот паттеррн очень полезен и обычно используется во многих библиотеках. Это позволяет вашему коду быть выразительным
|
||||
и менее многословным. По этой причине используйте цепочку методов и посмотрите, насколько чистым будет ваш код.
|
||||
|
@ -1723,7 +1721,7 @@ const query = new QueryBuilder()
|
|||
|
||||
### Принцип единой ответственности (SRP)
|
||||
|
||||
Как написано в Чистом Коде, "Должна быть лишь одна причина для изменения класса". аманчиво представить себе класс,
|
||||
Как написано в Чистом Коде, "Должна быть лишь одна причина для изменения класса". Заманчиво представить себе класс,
|
||||
переполненный большим количеством функционала, словно в полет вам позволили взять всего один чемодан. Проблема в том, что
|
||||
ваш класс не будет концептуально связан, и вы будете часто его изменять. Очень важно минимизировать изменения в классе.
|
||||
Когда вы вносите изменения в класс с огромным функционалом, тяжело отследить последствия ваших изменений.
|
||||
|
@ -2187,17 +2185,15 @@ await report = await reader.read('report.json');
|
|||
ветвлений обеспечивает высокое доверие к вашему коду и спокойствие всех разработчиков. Из этого следует, что в дополнение
|
||||
к отличному фреймворку для тестирования, необходимо также использовать хороший [инструмент покрытия](https://github.com/gotwarlost/istanbul).
|
||||
|
||||
Нет никакого оправдания, чтобы не писать тесты. Есть [много хороших фреймворков для тестирования на JS](http://jstherightway.org/#testing-tools) with typings support for TypeScript, so find one that your team prefers. When you find one that works for your team, then aim to always write tests for every new feature/module you introduce. If your preferred method is Test Driven Development (TDD), that is great, but the main point is to just make sure you are reaching your coverage goals before launching any feature, or refactoring an existing one.
|
||||
Нет никакого оправдания, чтобы не писать тесты. Есть [много хороших фреймворков для тестирования на JS](http://jstherightway.org/#testing-tools) с поддержкой типов для TypeScript, так что вы найдите тот который понравится вашей команде. Когда вы найдете тот, который работает для вашей команды, тогда стремитесь всегда писать тесты для каждой новой фичи/модуля, которую вы пишете. Если вы предпочитаете метод тест-ориентированной разработки (TDD), это замечательно, но главное - просто убедиться, что вы достигли своих целей покрытия, прежде чем запускать какую-либо функцию или реорганизовать существующую.
|
||||
|
||||
### Три закона TDD
|
||||
|
||||
1. Новый рабочий код пишется только после того, как будет написан модульный тест, который не проходит.
|
||||
|
||||
2. Вы пишете ровно такой объем кода модульного теста, какой необходим для того, чтобы этот тест не проходил (если код
|
||||
теста не компилируется, считается, что он не проходит).
|
||||
2. Вы пишете ровно такой объем кода модульного теста, какой необходим для того, чтобы этот тест не проходил (если код теста не компилируется, считается, что он не проходит).
|
||||
|
||||
3. Вы пишете ровно такой объем рабочего кода, какой необходим для прохождения модульного теста, который в данный момент
|
||||
не проходит.
|
||||
3. Вы пишете ровно такой объем рабочего кода, какой необходим для прохождения модульного теста, который в данный момент не проходит.
|
||||
|
||||
**[⬆ back to top](#содержание)**
|
||||
|
||||
|
@ -2205,27 +2201,22 @@ await report = await reader.read('report.json');
|
|||
|
||||
Чистые тесты должны следовать правилам:
|
||||
|
||||
- **Быстрота(Fast)** Тесты должны выполняться быстро. Все мы знаем, что разработчики люди, а люди ленивы, поскольку
|
||||
эти выражения являются “транзитивными”, то можно сделать вывод, что люди тоже ленивы. А ленивый человек не захочет
|
||||
запускать тесты при каждом изменении кода, если они будут долго выполняться.
|
||||
- **Быстрота(Fast)** Тесты должны выполняться быстро. Все мы знаем, что разработчики люди, а люди ленивы, поскольку эти выражения являются “транзитивными”, то можно сделать вывод, что люди тоже ленивы. А ленивый человек не захочет запускать тесты при каждом изменении кода, если они будут долго выполняться.
|
||||
|
||||
- **Независимость(Independent)** Тесты не должны зависеть друг от друга. Они должны обеспечивать одинаковые выходные
|
||||
данные независимо от того, выполняются ли они независимо или все вместе в любом порядке.
|
||||
- **Независимость(Independent)** Тесты не должны зависеть друг от друга. Они должны обеспечивать одинаковые выходные данные независимо от того, выполняются ли они независимо или все вместе в любом порядке.
|
||||
|
||||
- **Повторяемость(Repeatable)** Тесты должны выполняться в любой среде, и не должно быть никаких оправданий тому, почему
|
||||
они провалились.
|
||||
|
||||
- **Очевидность(Self-Validating)** Тест должен отвечать либо *Passed*, либо *Failed*. Вам не нужно сравнивать файлы
|
||||
журналов, чтобы ответить, что тест пройден.
|
||||
- **Очевидность(Self-Validating)** Тест должен отвечать либо *Passed*, либо *Failed*. Вам не нужно сравнивать файлы логов, дл чтобы ответить, что тест пройден.
|
||||
|
||||
- **Своевременность(Timely)** Юнит тесты должны быть написаны перед производственным кодом. Если вы пишете тесты после
|
||||
производственного кода, то вам может показаться, что писать тесты слишком сложно.
|
||||
- **Своевременность(Timely)** Юнит тесты должны быть написаны перед производственным кодом. Если вы пишете тесты после производственного кода, то вам может показаться, что писать тесты слишком сложно.
|
||||
|
||||
**[⬆ back to top](#содержание)**
|
||||
|
||||
### Один кейс на тест
|
||||
|
||||
Тесты также должны соответствовать *Принципу единой ответственности(SPP)*. Делайте только одно утверждение за единицу теста.
|
||||
Тесты также должны соответствовать *Принципу единой ответственности(SPP)*. Делайте только одно утверждение за единицу теста.(ps. не пренебрегайте этим правилом)
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
@ -2309,11 +2300,10 @@ describe('Calendar', () => {
|
|||
|
||||
## Асинхронность
|
||||
|
||||
### Предпочитайте promises а не callbacks
|
||||
### Используйте promises а не callbacks
|
||||
|
||||
Callback-функции ухудшают читаемость и приводят к чрезмерному количеству вложенности *(ад обратных вызовов(callback hell))*. Существуют утилиты, которые преобразуют существующие функции, используя стиль callback-ов, в версию, которая возвращает промисы (для Node.js смотрите [`util.promisify`](https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original), для общего назначения смотрите [pify](https://www.npmjs.com/package/pify), [es6-promisify](https://www.npmjs.com/package/es6-promisify))
|
||||
|
||||
Callback-функции ухудшают читаемость и приводят к чрезмерному количеству вложенности *(ад обратных вызовов(callback hell))*.
|
||||
Существуют утилиты, которые преобразуют существующие функции, используя стиль callback-ов, в версию, которая возвращает промисы
|
||||
(для Node.js смотрите [`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))
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
@ -2431,16 +2421,12 @@ try {
|
|||
## Обработка ошибок
|
||||
|
||||
Бросать ошибки — хорошее решение! Это означает, что во время выполнения вы будете знать, если что-то пошло не так, вы
|
||||
сможете остановить выполнение вашего приложения убивая процесс (в Node) в нужный момент и видеть место ошибки с помощью
|
||||
сможете остановить выполнение вашего приложения убив процесс (в Node) в нужный момент и увидеть место ошибки с помощью
|
||||
стек трейса в консоли.
|
||||
|
||||
### Всегда используйте ошибку для отклонения
|
||||
### Всегда используйте ошибки для отклонений(reject)
|
||||
|
||||
JavaScript и TypeScript позволяют вам делать `throw` любым объектом. Промис также может быть отклонен с любым объектом
|
||||
причины. Рекомендуется использовать синтаксис `throw` с типом `Error`. Это потому, что ваша ошибка может быть поймана
|
||||
в более высоком уровне кода с синтаксисом `catch`.
|
||||
Было бы очень странно поймать там строковое сообщение и сделать [отладку более болезненной](https://basarat.gitbooks.io/typescript/docs/types/exceptions.html#always-use-error).
|
||||
По той же причине вы должны отклонять промисы с типами `Error`.
|
||||
JavaScript и TypeScript позволяют вам делать `throw` любым объектом. Промис также может быть отклонен с любым объектом причины. Рекомендуется использовать синтаксис `throw` с типом `Error`. Это потому что ваша ошибка может быть поймана в более высоком уровне кода с синтаксисом `catch`. Было бы очень странно поймать там строковое сообщение и сделать [отладку более болезненной](https://basarat.gitbooks.io/typescript/docs/types/exceptions.html#always-use-error). По той же причине вы должны отклонять промисы с типами `Error`.
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
@ -2472,10 +2458,7 @@ async function get(): Promise<Item[]> {
|
|||
}
|
||||
```
|
||||
|
||||
Преимущество использования типов `Error` заключается в том, что они поддерживается синтаксисом `try/catch/finally`
|
||||
и неявно всеми ошибками и имеют свойство `stack`, которое является очень мощным для отладки.
|
||||
Есть и другие альтернативы: не использовать синтаксис `throw` и вместо этого всегда возвращать пользовательские объекты
|
||||
ошибок. TypeScript делает это еще проще.
|
||||
Преимущество использования типов `Error` заключается в том, что они поддерживается синтаксисом `try/catch/finally` и неявно всеми ошибками и имеют свойство `stack`, которое является очень мощным для отладки. Есть и другие альтернативы: не использовать синтаксис `throw` и вместо этого всегда возвращать пользовательские объекты ошибок. TypeScript делает это еще проще.
|
||||
Рассмотрим следующий пример:
|
||||
|
||||
```ts
|
||||
|
@ -2499,10 +2482,7 @@ function calculateTotal(items: Item[]): Failable<number, 'empty'> {
|
|||
|
||||
### Не игнорируйте отловленные ошибки
|
||||
|
||||
Игнорирование пойманной ошибки не дает вам возможности исправить или каким-либо образом отреагировать на ее появление.
|
||||
Логирование ошибок в консоль (`console.log`) не намного лучше, так как зачастую оно может потеряться в море консольных
|
||||
записей. Оборачивание куска кода в `try/catch` означает, что вы предполагаете возможность появления ошибки и имеете на
|
||||
этот случай четкий план.
|
||||
Игнорирование пойманной ошибки не дает вам возможности исправить или каким-либо образом отреагировать на ее появление. Логирование ошибок в консоль (`console.log`) не намного лучше, так как зачастую оно может потеряться в море консольных записей. Оборачивание куска кода в `try/catch` означает, что вы предполагаете возможность появления ошибки и имеете на этот случай четкий план.
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
@ -2579,15 +2559,10 @@ try {
|
|||
|
||||
## Форматирование
|
||||
|
||||
Форматирование носит субъективный характер. Как и во многом собранном здесь, в вопросе форматирования нет жестких правил,
|
||||
которым вы обязаны следовать. Главное - *НЕ СПОРИТЬ* по поводу форматирования. Есть множество инструментов для автоматизации
|
||||
этого. Используйте один! Это трата времени и денег когда инженеры спорят о форматировании. Общее правило, которому стоит
|
||||
следовать *соблюдайте правила форматирования*
|
||||
Форматирование носит субъективный характер. Как и во многом собранном здесь, в вопросе форматирования нет жестких правил, которым вы обязаны следовать. Главное - *НЕ СПОРИТЬ* по поводу форматирования. Есть множество инструментов для автоматизации этого. Используйте один! Это трата времени и денег когда инженеры спорят о форматировании. Общее правило, которому стоит следовать *соблюдайте правила форматирования принятые в команде*
|
||||
|
||||
Для TypeScript есть мощный инструмент под названием [TSLint](https://palantir.github.io/tslint/).
|
||||
Это статический анализ инструмент, который может помочь вам значительно улучшить читаемость и поддерживаемость вашего кода.
|
||||
Но лучще используйте [ESLint](https://github.com/typescript-eslint/typescript-eslint), так как TSLint больше не поддерживается.
|
||||
Есть готовые к использованию конфигурации TSLint, на которые вы можете ссылаться в своих проектах:
|
||||
Для TypeScript есть мощный инструмент под названием [TSLint](https://palantir.github.io/tslint/). Это статический анализ инструмент, который может помочь вам значительно улучшить читаемость и поддерживаемость вашего кода. Но лучще используйте [ESLint](https://github.com/typescript-eslint/typescript-eslint), так как TSLint больше не поддерживается.
|
||||
Есть готовые к использованию конфигурации TSLint и ESLint, на которые вы можете ссылаться в своих проектах:
|
||||
|
||||
- [TSLint Config Standard](https://www.npmjs.com/package/tslint-config-standard) - стандартный набор правил
|
||||
|
||||
|
@ -2607,8 +2582,7 @@ try {
|
|||
|
||||
### Используйте один вариант именования
|
||||
|
||||
Использование заглавных букв говорит вам о ваших переменных, функциях и др.. Эти правила субъективны, поэтому ваша команда
|
||||
может выбрать все, что они хотят. Дело в том, что независимо от того, что вы все выберите, просто *будьте последовательны*.
|
||||
Использование заглавных букв говорит вам о ваших переменных, функциях и др.. Эти правила субъективны, поэтому ваша команда может выбирать все, что они хотят. Дело в том, что независимо от того, что вы все выберите, просто *будьте последовательны*.
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
@ -2642,8 +2616,7 @@ type Animal = { /* ... */ }
|
|||
type Container = { /* ... */ }
|
||||
```
|
||||
|
||||
Предпочитайте использовать `PascalCase` для имен классов, интерфейсов, типов и пространств имен.
|
||||
Предпочитаю использовать `camelCase` для переменных, функций и членов класса.
|
||||
Предпочитайте использовать `PascalCase` для имен классов, интерфейсов, типов и пространств имен. Предпочитаю использовать `camelCase` для переменных, функций и членов класса.
|
||||
|
||||
**[⬆ back to top](#содержание)**
|
||||
|
||||
|
@ -2787,7 +2760,6 @@ import { ConfigPlugin } from './plugins/config/configPlugin';
|
|||
### Используйте typescript алиасы
|
||||
|
||||
Создайте более симпатичный импорт, определив пути и свойства baseUrl в разделе compilerOptions в `tsconfig.json`
|
||||
|
||||
Это позволит избежать длинных относительных путей при импорте.
|
||||
|
||||
**Плохо:**
|
||||
|
@ -2827,7 +2799,7 @@ import { UserService } from '@services/UserService';
|
|||
|
||||
### Предпочитаю понятный код вместо комментариев
|
||||
|
||||
Комментарии - это извинение, а не требование. Хороший код *в основном* сам документирует себя.
|
||||
Комментарии - это извинения, а не требование. Хороший код *в основном* сам документирует себя.
|
||||
|
||||
**Плохо:**
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue