"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Comprender a los decoradores en TypeScript: un enfoque de primeros principios

Comprender a los decoradores en TypeScript: un enfoque de primeros principios

Publicado el 2024-11-01
Navegar:643

Understanding Decorators in TypeScript: A First-Principles Approach

Los decoradores en TypeScript proporcionan un mecanismo poderoso para modificar el comportamiento de clases, métodos, propiedades y parámetros. Si bien pueden parecer una conveniencia moderna, los decoradores tienen sus raíces en el patrón de decorador bien establecido que se encuentra en la programación orientada a objetos. Al abstraer funciones comunes como el registro, la validación o el control de acceso, los decoradores permiten a los desarrolladores escribir código más limpio y fácil de mantener.

En este artículo, exploraremos los decoradores desde sus primeros principios, analizaremos su funcionalidad principal y los implementaremos desde cero. A lo largo del camino, veremos algunas aplicaciones del mundo real que muestran la utilidad de los decoradores en el desarrollo cotidiano de TypeScript.

¿Qué es un decorador?

En TypeScript, un decorador es simplemente una función que se puede adjuntar a una clase, método, propiedad o parámetro. Esta función se ejecuta en tiempo de diseño, lo que le brinda la posibilidad de modificar el comportamiento o la estructura del código antes de ejecutarlo. Los decoradores permiten la metaprogramación, lo que nos permite agregar funcionalidades adicionales sin modificar la lógica original.

Comencemos con un ejemplo simple de un decorador de métodos que registra cuando se llama a un método:

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with arguments: ${args}`);
    return originalMethod.apply(this, args);
  };

  return descriptor;
}

class Example {
  @log
  greet(name: string) {
    return `Hello, ${name}`;
  }
}

const example = new Example();
example.greet('John');

Aquí, el decorador de registros envuelve el método de bienvenida y registra su invocación y parámetros antes de ejecutarlo. Este patrón es útil para separar preocupaciones transversales, como el registro, de la lógica central.

Cómo trabajan los decoradores

Los decoradores en TypeScript son funciones que toman metadatos relacionados con el elemento que están decorando. Con base en estos metadatos (como prototipos de clases, nombres de métodos o descriptores de propiedades), los decoradores pueden modificar el comportamiento o incluso reemplazar el objeto decorado.

Tipos de decoradores

Los decoradores se pueden aplicar a varios objetivos, cada uno con diferentes propósitos:

  • Decoradores de clases : Una función que recibe el constructor de la clase.
function classDecorator(constructor: Function) {
  // Modify or extend the class constructor or prototype
}
  • Decoradores de métodos: una función que recibe el objeto de destino, el nombre del método y el descriptor del método.
function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // Modify the method's descriptor
}
  • Decoradores de propiedades: una función que recibe el objeto de destino y el nombre de la propiedad.
function propertyDecorator(target: any, propertyKey: string) {
  // Modify the behavior of the property
}
  • Decoradores de parámetros: una función que recibe el objetivo, el nombre del método y el índice del parámetro.
function parameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
  // Modify or inspect the method's parameter
}

Pasar argumentos a los decoradores

Una de las características más poderosas de los decoradores es su capacidad para aceptar argumentos, lo que le permite personalizar su comportamiento. Por ejemplo, creemos un decorador de métodos que registre las llamadas a métodos de forma condicional en función de un argumento.

function logConditionally(shouldLog: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      if (shouldLog) {
        console.log(`Calling ${propertyKey} with arguments: ${args}`);
      }
      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

class Example {
  @logConditionally(true)
  greet(name: string) {
    return `Hello, ${name}`;
  }
}

const example = new Example();
example.greet('TypeScript Developer');

Al pasar true al decorador logConditionally, nos aseguramos de que el método registre su ejecución. Si pasamos falso, se omite el registro. Esta flexibilidad es clave para que los decoradores sean versátiles y reutilizables.

Aplicaciones de los decoradores en el mundo real

Los decoradores han encontrado un uso práctico en muchas bibliotecas y marcos. A continuación se muestran algunos ejemplos notables que ilustran cómo los decoradores optimizan funciones complejas:

  • Validación en class-validator: en aplicaciones basadas en datos, la validación es crucial. El paquete class-validator utiliza decoradores para simplificar el proceso de validación de campos en clases de TypeScript.
import { IsEmail, IsNotEmpty } from 'class-validator';

class User {
  @IsNotEmpty()
  name: string;

  @IsEmail()
  email: string;
}

En este ejemplo, los decoradores @IsEmail y @IsNotEmpty garantizan que el campo de correo electrónico sea una dirección de correo electrónico válida y que el campo de nombre no esté vacío. Estos decoradores ahorran tiempo al eliminar la necesidad de una lógica de validación manual.

  • Mapeo relacional de objetos con TypeORM: los decoradores se usan ampliamente en marcos ORM como TypeORM para asignar clases de TypeScript a tablas de bases de datos. Este mapeo se realiza de forma declarativa utilizando decoradores.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

Aquí, @Entity, @Column y @PrimaryGeneratedColumn definen la estructura de la tabla de usuarios. Estos decoradores abstraen la complejidad de la creación de tablas SQL, lo que hace que el código sea más legible y fácil de mantener.

  • Inyección de dependencia angular: en Angular, los decoradores desempeñan un papel fundamental en la gestión de servicios y componentes. El decorador @Injectable marca una clase como servicio que se puede inyectar en otros componentes o servicios.
@Injectable({
  providedIn: 'root',
})
class UserService {
  constructor(private http: HttpClient) {}
}

El decorador @Injectable en este caso indica al sistema de inyección de dependencia de Angular que el servicio de usuario debe proporcionarse globalmente. Esto permite una integración perfecta de los servicios en toda la aplicación.

Implementar sus propios decoradores: un desglose

Los decoradores son, en esencia, solo funciones. Analicemos el proceso de creación de decoradores desde cero:

Decorador de clase

Un decorador de clase recibe el constructor de la clase y puede usarse para modificar el prototipo de clase o incluso reemplazar el constructor.

function AddTimestamp(constructor: Function) {
  constructor.prototype.timestamp = new Date();
}

@AddTimestamp
class MyClass {
  id: number;
  constructor(id: number) {
    this.id = id;
  }
}

const instance = new MyClass(1);
console.log(instance.timestamp);  // Outputs the current timestamp

Decorador de métodos

Un decorador de métodos modifica el descriptor del método, permitiéndole alterar el comportamiento del método en sí.

function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();
    console.log(`${propertyKey} executed in ${end - start}ms`);
    return result;
  };

  return descriptor;
}

class Service {
  @logExecutionTime
  execute() {
    // Simulate work
    for (let i = 0; i 



Decorador de propiedades

Un decorador de propiedades le permite interceptar el acceso y la modificación de propiedades, lo que puede resultar útil para realizar un seguimiento de los cambios.

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

  const getter = () => value;
  const setter = (newValue: any) => {
    console.log(`${propertyKey} changed from ${value} to ${newValue}`);
    value = newValue;
  };

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
  });
}

class Product {
  @trackChanges
  price: number;

  constructor(price: number) {
    this.price = price;
  }
}

const product = new Product(100);
product.price = 200;  // Logs the change

Conclusión

Los decoradores en TypeScript le permiten abstraer y reutilizar funciones de una manera limpia y declarativa. Ya sea que esté trabajando con validación, ORM o inyección de dependencias, los decoradores ayudan a reducir el texto repetitivo y a mantener su código modular y mantenible. Comprender cómo funcionan desde los primeros principios hace que sea más fácil aprovechar todo su potencial y crear soluciones personalizadas adaptadas a su aplicación.

Al observar más profundamente la estructura y las aplicaciones del mundo real de los decoradores, ahora ha visto cómo pueden simplificar tareas de codificación complejas y optimizar el código en varios dominios.

Declaración de liberación Este artículo se reproduce en: https://dev.to/curious_tinkerer/understanding-decorators-in-typescript-a-first-principles-approach-2n3h?1 Si hay alguna infracción, comuníquese con [email protected] para eliminar él
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3