Тепер пишіть чистіший і розумніший код

TypeScript — це мова програмування зі статичною типізацією, розроблена на базі JavaScript. Вона пропонує розширені можливості для розробки великих проєктів. TypeScript був створений для вирішення деяких проблем, які можуть виникати під час написання коду на JavaScript. Завдяки використанню типів даних, TypeScript допомагає уникнути багатьох типових помилок.

У коді на TypeScript кожне значення має певний тип. TypeScript виконує перевірку, щоб впевнитись, що кожне значення відповідає правилам, встановленим для його типу. Ця перевірка дозволяє виявити помилки у вихідному коді ще до запуску програми.

Такий процес називається статичною перевіркою типів, яка полягає у виявленні помилок на етапі розробки на основі типів даних, що використовуються в програмі.

Окрім надання статичної перевірки типів і сприяння написанню зрозумілішого коду, TypeScript також пропонує інші можливості для поліпшення читабельності коду, його повторного використання та обслуговування. Однією з таких функцій є декоратори.

Декоратори TypeScript

Декоратори в TypeScript — це спеціальні інструменти, що дозволяють змінювати або вдосконалювати поведінку коду під час його виконання, а також додавати метадані до коду. Завдяки декораторам, в TypeScript стає можливим метапрограмування. Метапрограмування — це методика, за якої програми можуть оперувати іншими програмами, розглядаючи їх як власні дані, і таким чином впливати на їх поведінку.

Фактично, декоратори — це функції, які викликаються для виконання певних операцій під час доступу або зміни елементів, до яких вони застосовані.

Це дозволяє додавати додатковий функціонал до декорованих елементів. Декоратори TypeScript можна використовувати з оголошеннями класів, методів, властивостей, гетерів і сеттерів, а також з параметрами методів.

У TypeScript декоратори починаються зі знаку @ та мають вигляд @вираз, де вираз обчислюється у функцію, що буде викликана під час виконання. Загальний синтаксис використання декораторів у TypeScript виглядає так:

@decoratorName
itemToDecorate

Нижче наведено приклад простого декоратора класу:

function logClass(target: Function) {
  console.log("Викликано декоратор класу Log Class");
  console.log("Клас:", target);
}

@logClass // @logClass - це декоратор
class MyClass {
  constructor() {
    console.log("Створено екземпляр класу MyClass");
  }
}

const myInstance = new MyClass();

Результат виконання цього коду буде наступним:

Вивід:

Викликано декоратор класу Log Class
Клас: [class MyClass]
Створено екземпляр класу MyClass

Функція `logClass()` приймає один аргумент під назвою `target`, тип якого — `Function`. Аргумент `target` має тип `Function`, оскільки він отримує конструктор класу, який ми декоруємо.

Щоб використати функцію `logClass()` як декоратор для класу `MyClass`, ми ставимо `@logclass` безпосередньо перед оголошенням класу `MyClass`. Декоратор повинен мати таке саме ім’я, як і функція, яку ви хочете використовувати для декорування елемента.

Коли створюється екземпляр `MyClass`, крім конструктора класу, виконується і логіка декоратора, як це видно у виводі.

Наразі декоратори є експериментальною функцією в TypeScript. Тому, щоб використовувати декоратори, потрібно увімкнути їх в опціях компілятора в файлі `tsconfig.json`.

Для цього потрібно створити файл `tsconfig.json` у папці вашого TypeScript проєкту, виконавши в терміналі наступну команду:

tsc --init

Після створення файлу `tsconfig.json`, відкрийте його та розкоментуйте опцію `experimentalDecorators`, як показано нижче:

Крім цього, встановіть мінімальну версію JavaScript як ES2015.

Значення декораторів TypeScript

Якісний код легко розпізнати за тим, наскільки він читабельний, придатний для повторного використання та легкий в обслуговуванні. Читабельний код — це код, який легко зрозуміти та інтерпретувати. Він чітко передає наміри розробника.

Багаторазовий код — це код, який дозволяє використовувати певні компоненти, такі як функції та класи, в різних частинах програми або в інших проєктах без істотних змін.

Підтримуваний код — це код, який можна легко модифікувати, оновлювати та виправляти протягом усього його життєвого циклу.

Декоратори TypeScript дозволяють досягти читабельності коду, можливості його повторного використання та простоти обслуговування. По-перше, вони дозволяють поліпшити поведінку коду за допомогою декларативного синтаксису, який легше читати. Логіку можна інкапсулювати в декоратори і викликати їх, застосовуючи до різних елементів коду, де це потрібно.

Це робить код зрозумілішим та чітко передає наміри розробника. Декоратори не є одноразовими компонентами. Їх можна використовувати багаторазово. Ви можете створити декоратор один раз і використовувати його в багатьох місцях.

Ви можете визначати декоратори, імпортувати їх і застосовувати у будь-якій частині вашого проєкту, де потрібно змінити поведінку коду. Це вигідно, оскільки дозволяє уникнути дублювання логіки, підвищуючи можливість повторного використання коду.

Декоратори також забезпечують гнучкість та модульність, дозволяючи розділяти різні функції на незалежні компоненти. Все це, в поєднанні з тим, що вони допомагають писати зрозумілий і багаторазовий код, робить декоратори TypeScript важливим інструментом для створення коду, який легко підтримувати.

Типи декораторів TypeScript

Як вже було зазначено, декоратори TypeScript можна використовувати з класами, властивостями класів, методами класів, аксесорами класів та параметрами методів класів. Відповідно до елементів, які можна декорувати, виділяють різні типи декораторів TypeScript. До таких декораторів належать:

#1. Декоратор класу

Декоратор класу — це декоратор, який використовується для спостереження, зміни або заміни визначення класу. Він розміщується безпосередньо перед класом, який декорує. Декоратор класу застосовується до конструктора класу. Під час виконання декоратор класу викликається з одним аргументом — конструктором класу, який він декорує.

Нижче показаний декоратор класу, який використовується для запобігання розширенню класу:

function frozen(target: Function) {
  Object.freeze(target);
  Object.freeze(target.prototype)
}

@frozen
class Vehicle {
  wheels: number = 4;
  constructor() {
    console.log("Транспортний засіб створено")
  }
}

class Car extends Vehicle {
  constructor() {
    super();
    console.log("Автомобіль створено");
  }
}

console.log(Object.isFrozen(Vehicle));

Щоб запобігти розширенню класу, використовується функція `Object.freeze()`, якій передається клас. Декоратор використовується для додавання цієї функції до класу. Можна перевірити, чи клас `Vehicle` заморожено, передавши клас до `isFrozen()`. Вивід коду показано нижче:

true

#2. Декоратор властивості

Декоратор властивості використовується для декорування властивості класу. Він оголошується безпосередньо перед властивістю. Декоратори властивостей можна використовувати для зміни або спостереження за визначенням властивості.

Під час виконання буде викликано декоратор, який приймає два аргументи. Перший аргумент — це функція-конструктор класу (якщо член статичний) або прототип класу (якщо він є членом екземпляра). Другий аргумент — це ім’я члена (тобто властивості, яку ви декоруєте).

У TypeScript статичні члени позначаються ключовим словом `static`. До статичних членів можна звертатися без створення екземпляра класу. Члени екземпляра не мають префікса `static`, і до них можна звертатися лише після створення екземпляра класу.

Нижче наведено приклад декоратора властивості:

function wheelsDecorator(target: any, propertyName: string) {
  console.log(propertyName.toUpperCase())
}

class Vehicle {
  @wheelsDecorator
  wheels: number = 4;
  constructor() {
    console.log("Транспортний засіб створено")
  }
}

Результат виконання коду буде наступним:

WHEELS

#3. Декоратор методу

Декоратор методу оголошується безпосередньо перед оголошенням методу. Він використовується для спостереження, зміни або заміни визначення методу. Він приймає три аргументи: функцію-конструктор класу (якщо член статичний) або прототип класу (якщо він є членом екземпляра), ім’я члена і дескриптор властивості для цього елемента. Дескриптор властивості — це об’єкт, пов’язаний з властивостями об’єкта. Він надає інформацію про атрибути та поведінку властивості.

Декоратори методів корисні, коли потрібно виконати певні дії до або після виклику методу. Також їх можна використовувати для реєстрації інформації про викликаний метод. Це може бути корисно, якщо потрібно повідомити користувача про те, що метод застарів. Тобто, що він ще доступний для використання, але його не рекомендується застосовувати, оскільки пізніше його можуть видалити.

Приклад декоратора методу показано нижче:

const logDeprecated =(target: any, methodName: string, descriptor: PropertyDescriptor) => {
  console.log(`${methodName} застарів`)
  console.log(descriptor);
}

class Vehicle {
  wheels: number = 4;
  constructor() {
    console.log("Транспортний засіб створено")
  }
  @logDeprecated
  reFuel(): void {
    console.log("Ваш транспортний засіб заправляється");
  }
}

Вивід:

reFuel застарів
{
  value: [Function: reFuel],
  writable: true,
  enumerable: false,
  configurable: true
}

#4. Декоратори аксесорів

У TypeScript є два типи аксесорів: `get` і `set`. Аксесори використовуються для керування доступом до властивостей класу. Декоратори аксесорів використовуються для декорування цих двох методів. Вони оголошуються безпосередньо перед оголошенням аксесорів. Оскільки аксесори все ще є методами, декоратори аксесорів працюють так само, як і декоратори методів.

Нижче наведено приклад декораторів аксесорів:

const logWheels =(target: any, accessorName: string, descriptor: PropertyDescriptor) => {
  console.log(`${accessorName} використовується для отримання кількості коліс`)
  console.log(descriptor);
}

class Vehicle {
  private wheels: number = 4;
  constructor() {
    console.log("Транспортний засіб створено")
  }
  @logWheels
  get numWheels(): number {
    return this.wheels;
  }
}

Вивід:

numWheels використовується для отримання кількості коліс
{
  get: [Function: get numWheels],
  set: undefined,
  enumerable: false,
  configurable: true
}

Стосовно декораторів аксесорів, важливо відзначити, що вони не можуть бути застосовані до декількох аксесорів `get/set` з однаковою назвою. Наприклад, у нашому коді вище, якщо ви створите сеттер з назвою `set numWheels`, ви не зможете використати для нього декоратор `logWheels`.

#5. Декоратори параметрів

Декоратор параметрів використовується для спостереження за тим, що параметр був оголошений в методі. Він оголошується перед оголошенням параметра. Декоратори параметрів приймають три аргументи: функцію-конструктор класу (для статичного члена) або прототип класу (для члена екземпляра). Другий аргумент — це ім’я члена (тобто ім’я параметра). Третій аргумент — це порядковий індекс параметра у списку параметрів функції. Тобто, яка позиція параметра у списку параметрів, де перший параметр має індекс 0?

Нижче наведено приклад декоратора параметрів:

const passengerLog = (target: Object, propertyKey: string, parameterIndex: number) => {
  console.log(`Декоратор параметра ${propertyKey} з індексом ${parameterIndex}`);
}

class Vehicle {
  private wheels: number = 4;
  constructor() {
    console.log("Транспортний засіб створено")
  }
  pickPassenger( location: string, numPassengers: string, @passengerLog driver: string) {
    console.log(`${numPassengers} підібрано в ${location} водієм ${driver}`)
  }
  dropPassenger(driver: string, @passengerLog location: string, numPassengers: string) {
    console.log(`${numPassengers} висаджено в ${location} водієм ${driver}`)
  }
}

Вивід:

Декоратор параметра pickPassenger з індексом 2
Декоратор параметра dropPassenger з індексом 1

Висновок

Декоратори TypeScript значно покращують читабельність коду та допомагають писати модульний і багаторазовий код, оскільки декоратори можна оголошувати один раз і використовувати багато разів. Також декоратори сприяють загальній ремонтопридатності коду.

Хоча декоратори все ще є експериментальною функцією, вони є дуже корисними. Вам варто з ними ознайомитися.

Також можете прочитати, як перетворити рядок на число в TypeScript.