Los decoradores son una característica poderosa y avanzada de TypeScript que permiten modificar el comportamiento de clases, métodos, propiedades y parámetros de una forma declarativa. Son ampliamente utilizados en frameworks como Angular, pero también pueden emplearse en proyectos personalizados para mejorar la reutilización y la claridad del código. En este artículo exploraremos los decoradores avanzados en TypeScript, sus usos más comunes, cómo crearlos desde cero, y las mejores prácticas para implementarlos.
Introducción a los decoradores Link to heading
¿Qué son los decoradores? Link to heading
Un decorador es una función especial que puede adjuntarse a una clase, método, accesor, propiedad o parámetro. Proporcionan una forma de agregar meta-programación a tu código, es decir, modificar su comportamiento sin cambiar su estructura básica.
Tipos de decoradores en TypeScript Link to heading
- Decoradores de clases:
- Aplicados a clases completas para modificar su estructura o comportamiento.
- Decoradores de métodos:
- Aplicados a métodos de clase para interceptar, modificar o registrar su ejecución.
- Decoradores de propiedades:
- Aplicados a propiedades de clase para añadir lógica adicional al acceso o modificación del valor.
- Decoradores de parámetros:
- Aplicados a parámetros de métodos para realizar validaciones o registrar su uso.
Habilitar decoradores en TypeScript Link to heading
Para usar decoradores, es necesario habilitar la opción experimentalDecorators
en el archivo tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
Decoradores de clases Link to heading
Un decorador de clase se aplica a la definición de una clase completa. Es ideal para añadir funcionalidades transversales como registros o validaciones.
Ejemplo básico Link to heading
function LogClass(target: Function) {
console.log(`Clase decorada: ${target.name}`);
}
@LogClass
class MyClass {
constructor() {
console.log('Instancia creada');
}
}
const instance = new MyClass();
Resultado Link to heading
Clase decorada: MyClass
Instancia creada
Uso avanzado: Agregar metadatos Link to heading
function AddMetadata(metadata: any) {
return function (target: Function) {
target.prototype.metadata = metadata;
};
}
@AddMetadata({ role: 'admin', permissions: ['read', 'write'] })
class User {}
const user = new User();
console.log(user['metadata']);
Decoradores de métodos Link to heading
Permiten interceptar y modificar la ejecución de un método. Esto es útil para agregar registros, manejo de errores o validaciones.
Ejemplo: Registro de ejecución Link to heading
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Método ejecutado: ${propertyKey}, con argumentos: ${args}`);
return originalMethod.apply(this, args);
};
}
class Calculator {
@LogMethod
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3);
Resultado Link to heading
Método ejecutado: add, con argumentos: 2,3
Decoradores de propiedades Link to heading
Permiten personalizar el comportamiento de las propiedades de clase, por ejemplo, agregando validaciones o valores predeterminados.
Ejemplo: Validar propiedades Link to heading
function MinLength(length: number) {
return function (target: any, propertyKey: string) {
let value: string;
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue: string) => {
if (newValue.length < length) {
throw new Error(`La longitud mínima de ${propertyKey} es ${length}`);
}
value = newValue;
},
});
};
}
class User {
@MinLength(5)
username: string;
}
const user = new User();
user.username = 'John'; // Error: La longitud mínima de username es 5
Decoradores de parámetros Link to heading
Estos decoradores son útiles para realizar validaciones en los parámetros de métodos.
Ejemplo: Validación de parámetros Link to heading
function Validate(target: any, propertyKey: string, parameterIndex: number) {
const existingRequiredParameters: number[] = Reflect.getOwnMetadata('required', target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata('required', existingRequiredParameters, target, propertyKey);
}
class OrderService {
processOrder(@Validate orderId: string) {
console.log(`Procesando orden: ${orderId}`);
}
}
Buenas prácticas con decoradores Link to heading
- Mantén los decoradores reutilizables:
- Diseña decoradores genéricos que puedan ser aplicados en múltiples contextos.
- Evita la lógica compleja:
- Mantén los decoradores simples y delega la lógica compleja a otras partes del código.
- Documenta los decoradores:
- Proporciona descripciones claras sobre el propósito y uso de cada decorador.
- Combina decoradores con metadatos:
- Usa bibliotecas como
reflect-metadata
para almacenar y acceder a metadatos en tiempo de ejecución.
- Usa bibliotecas como
- Prueba los decoradores:
- Escribe pruebas unitarias para garantizar que los decoradores funcionen correctamente en diferentes escenarios.
Conclusión Link to heading
Los decoradores en TypeScript son una herramienta poderosa que puede transformar la forma en que organizas y escribes tu código. Desde agregar metadatos hasta interceptar métodos y propiedades, ofrecen una manera elegante y declarativa de manejar lógica transversal. Siguiendo las buenas prácticas y explorando casos de uso avanzados, puedes aprovechar al máximo esta característica y llevar tu desarrollo TypeScript al siguiente nivel.