TypeScript - Декораторы

Здравствуйте, ambitные программисты! Сегодня мы отправимся в увлекательное путешествие в мир декораторов TypeScript. Не волнуйтесь, если вы новички в программировании - я буду вести вас шаг за шагом через это понятие, как я делал это для countless студентов на протяжении многих лет моей преподавательской деятельности. Итак, погружаемся!

TypeScript - Decorators

Что такое декораторы?

Прежде чем углубиться в детали, давайте поймем, что такое декораторы. Представьте себе обычный кекс. Декораторы resemble посыпку, глазурь и вишенку на вершине, которые делают ваш кекс extra special. В TypeScript декораторы добавляют extra функциональность вашим классам, методам, свойствам и параметрам.

Использование декораторов в TypeScript

Чтобы начать использовать декораторы в TypeScript, вам нужно включить их в вашем файле tsconfig.json. Это как включение духовки перед выпечкой ваших кексов!

{
"compilerOptions": {
"experimentalDecorators": true
}
}

Синтаксис декораторов

Теперь давайте посмотрим, как мы пишем декораторы. Это проще, чем вы можете подумать!

function simpleDecorator(target: any) {
console.log("Я простой декоратор!");
}

@simpleDecorator
class MyClass {
// Реализация класса
}

В этом примере simpleDecorator resembles наклейку, которую мы клеим на наш MyClass. Каждый раз, когда мы используем MyClass, он будет выводить "Я простой декоратор!" в консоль.

Фабрики декораторов

Иногда мы хотим, чтобы наши декораторы были настраиваемыми. Вот где появляются фабрики декораторов. Они resemble machine, которая производит декораторы на основе ваших спецификаций.

function decoratorFactory(message: string) {
return function (target: any) {
console.log(message);
}
}

@decoratorFactory("Привет, я пользовательский декоратор!")
class MyClass {
// Реализация класса
}

Здесь decoratorFactory создает декоратор, который выводит ваше пользовательское сообщение.

Композиция декораторов

Мы можем использовать несколько декораторов для одной цели. Это resembles добавление нескольких начинок к вашему кексу!

function first() {
console.log("first(): фабрика оценена");
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("first(): вызван");
};
}

function second() {
console.log("second(): фабрика оценена");
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("second(): вызван");
};
}

class ExampleClass {
@first()
@second()
method() {}
}

В этом случае декораторы применяются снизу вверх: second() затем first().

Почему использовать декораторы?

Декораторы incredibly полезны для:

  1. Добавления метаданных к вашему коду
  2. Изменения поведения классов и методов
  3. Реализации аспектов Aspect-Oriented Programming
  4. Создания повторно используемого кода, который можно легко применить к нескольким классам

Декораторы классов

Декораторы классов применяются к конструктору класса и могут использоваться для наблюдения, изменения или замены определения класса.

function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}

@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}

В этом примере декоратор @sealed предотвращает изменение класса Greeter после его определения.

Методы декораторов

Методы декораторов могут использоваться для изменения, наблюдения или замены определения метода.

function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}

class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}

@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}

Здесь декоратор @enumerable(false) делает метод greet неудаляемым.

Декораторы доступа

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

function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}

class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}

@configurable(false)
get x() { return this._x; }

@configurable(false)
get y() { return this._y; }
}

В этом примере декоратор @configurable(false) делает доступы x и y неудаляемыми.

Декораторы свойств

Декораторы свойств используются для наблюдения, изменения или замены определения свойства.

function format(formatString: string) {
return function (target: any, propertyKey: string): any {
let value = target[propertyKey];

const getter = function () {
return `${formatString} ${value}`;
};

const setter = function (newVal: string) {
value = newVal;
};

return {
get: getter,
set: setter,
enumerable: true,
configurable: true
};
};
}

class Greeter {
@format("Hello,")
greeting: string;
}

const greeter = new Greeter();
greeter.greeting = "World";
console.log(greeter.greeting); // Выводит: "Hello, World"

Здесь декоратор @format добавляет префикс к свойству greeting.

Декораторы параметров

Декораторы параметров используются для наблюдения, изменения или замены определения параметра.

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
let existingRequiredParameters: number[] = Reflect.getOwnMetadata("required", target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata("required", existingRequiredParameters, target, propertyKey);
}

class Greeter {
greet(@required name: string) {
return "Hello " + name;
}
}

В этом примере декоратор @required помечает параметр name как обязательный.

Методы декораторов

Вот таблица, резюмирующая различные типы декораторов и их методы:

Тип декоратора Метод
Декоратор класса declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
Декоратор метода declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
Декоратор доступа declare type AccessorDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
Декоратор свойства declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
Декоратор параметра declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;

И вот мы covered основы декораторов TypeScript. Помните, как обучение выпекать идеальный кекс, овладение декораторами требует практики. Не бойтесь экспериментировать и делать ошибки - это как мы учимся и растем как программисты. Счастливого кодирования!

Credits: Image by storyset