Чистый код для TypeScript — Часть 1

от автора

Наткнувшись на материал по принципам чистый код для TypeScript и прочитав его решил взяться за его перевод. Здесь я хочу поделиться с вами некоторыми выдержками из этого перевода, так как некоторые моменты чистого кода для TypeScript повторяют такие же принципы для JavaScript, я их здесь описывать не буду, если будет интересно перевод для JS уже публиковался на хабре(@BoryaMogila) или же можете ознакомится с ними в первоисточнике.

Для начала давайте же разберемся что такое, эти принципы чистого кода. Но дать четкого определения чистого кода к сожалению вряд ли получится. Отчасти это все зависит от людей, так например приходишь в музей рассматриваешь картину и думаешь что за уродство, но тут же подходит другой человек и говорит, какое великолепие. Да у нас есть какие определенные, общие черты мира, где мы можем сказать, что то или иное красиво, но дать всему этому определение мы точно не сможем. Так и здесь это всего лишь какие то небольшие критерии этой красоты, соблюдение которых выбирает сам, так как. это не те правила которые высечены в граните. Это просто рекомендации.

Переменные

Используйте enum для документирования

Enam’ы могут помочь документированию вашего кода. Например когда мы обеспокоены тем, что наши переменные отличаются от значений.

Плохо:

const GENRE = {   ROMANTIC: 'romantic',   DRAMA: 'drama',   COMEDY: 'comedy',   DOCUMENTARY: 'documentary', }  projector.configureFilm(GENRE.COMEDY);  class Projector {   // delactation of Projector   configureFilm(genre) {     switch (genre) {       case GENRE.ROMANTIC:         // some logic to be executed      }   } }

Хорошо:

enum GENRE {   ROMANTIC,   DRAMA,   COMEDY,   DOCUMENTARY, }  projector.configureFilm(GENRE.COMEDY);  class Projector {   // delactation of Projector   configureFilm(genre) {     switch (genre) {       case GENRE.ROMANTIC:         // some logic to be executed      }   } }

Функции

Избегайте проверки типов

TypeScript является надмножеством синтаксиса JavaScript и добавляют дополнительные статические проверки типов для языка. Всегда предпочитайте указывать типы переменных, параметров и возвращаемых значений, чтобы использовать всю мощь TypeScript. Это делает будущий рефакторинг более легким.

Плохо:

function travelToTexas(vehicle: Bicycle | Car) {   if (vehicle instanceof Bicycle) {     vehicle.pedal(currentLocation, new Location('texas'));   } else if (vehicle instanceof Car) {     vehicle.drive(currentLocation, new Location('texas'));   } }

Хорошо:

type Vehicle = Bicycle | Car;  function travelToTexas(vehicle: Vehicle) {   vehicle.move(currentLocation, new Location('texas')); }

Используйте итераторы и генераторы

Используйте генераторы и итераторы при работе с коллекциями данных, которые используются как поток.
Есть несколько причин для этого:

  • отделяет вызываемый объект от реализации генератора в том смысле, что вызываемый объект решает сколько элементов
    иметь для доступа
  • ленивое выполнение, элементы передаются по требованию
  • встроенная поддержка итерации элементов с использованием синтаксиса for-of
  • итераторы позволяют реализовать оптимизированные паттерны итераторов

Плохо:

function fibonacci(n: number): number[] {   if (n === 1) return [0];   if (n === 2) return [0, 1];    const items: number[] = [0, 1];   while (items.length < n) {     items.push(items[items.length - 2] + items[items.length - 1]);   }    return items; }  function print(n: number) {   fibonacci(n).forEach(fib => console.log(fib)); }  // Print first 10 Fibonacci numbers. print(10);

Хорошо:

// Generates an infinite stream of Fibonacci numbers. // The generator doesn't keep the array of all numbers. function* fibonacci(): IterableIterator<number> {   let [a, b] = [0, 1];    while (true) {     yield a;     [a, b] = [b, a + b];   } }  function print(n: number) {   let i = 0;   for (const fib of fibonacci()) {     if (i++ === n) break;       console.log(fib);   }   }  // Print first 10 Fibonacci numbers. print(10);

Существуют библиотеки, которые позволяют работать с итераторами так же, как и с собственными массивами, путем цепочка методов, таких как map, slice, forEach и др. Смотрите itiriri пример продвинутой манипуляции с итераторами (или itiriri-async для манипуляции с асинхронными итераторами).

import itiriri from 'itiriri';  function* fibonacci(): IterableIterator<number> {   let [a, b] = [0, 1];    while (true) {     yield a;     [a, b] = [b, a + b];   } }  itiriri(fibonacci())   .take(10)   .forEach(fib => console.log(fib));

Объекты и структуры данных

Используйте геттеры и сеттеры

TypeScript поддерживает синтаксис геттеров и сеттеров. Использовать геттеры и сеттеры для доступа к данным объекта гораздо лучше, чем напрямую обращаться к его свойствам. "Почему?" спросите вы. Вот список причин:

  • Если вы хотите реализовать больше, чем просто доступ к свойству, вам нужно поменять реализацию в одном месте, а не по всему коду
  • Валидацию легко реализовать на уровне реализации set
  • Инкапсуляция внутреннего состояния
  • Легко добавить логирование и обработку ошибок на уровне геттеров и сеттеров
  • Вы можете лениво подгружать свойства вашего объекта, например, с сервера

Плохо:

type BankAccount = {   balance: number;   // ... }  const value = 100; const account: BankAccount = {   balance: 0,   // ... };  if (value < 0) {   throw new Error('Cannot set negative balance.'); }  account.balance = value;

Хорошо:

class BankAccount {   private accountBalance: number = 0;    get balance(): number {     return this.accountBalance;   }    set balance(value: number) {     if (value < 0) {       throw new Error('Cannot set negative balance.');     }      this.accountBalance = value;   }    // ... }  // Теперь `BankAccount` инкапсулирует логику проверки. // Если однажды спецификации изменятся, и нам понадобится дополнительное правило проверки, // нам придется изменить только реализацию `сеттера`, // оставив весь зависимый код без изменений. const account = new BankAccount(); account.balance = 100;

Создавайте объекты с приватными/защищенными полями

TypeScript поддерживает public (по умолчанию), protected и private средства доступа к свойствам класса.

Плохо:

class Circle {   radius: number;    constructor(radius: number) {     this.radius = radius;   }    perimeter() {     return 2 * Math.PI * this.radius;   }    surface() {     return Math.PI * this.radius * this.radius;   } }

Хорошо:

class Circle {   constructor(private readonly radius: number) {   }    perimeter() {     return 2 * Math.PI * this.radius;   }    surface() {     return Math.PI * this.radius * this.radius;   } }

Уважаемые читатели, а какими принципами вы пользуетесь при использовании TypeScript?

Продолжение следует…

ссылка на оригинал статьи https://habr.com/ru/post/484402/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *