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
¿Qué son los decoradores?
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
- 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
Para usar decoradores, es necesario habilitar la opción experimentalDecorators
en el archivo tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
Decoradores de clases
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
function LogClass(target: Function) {
console.log(`Clase decorada: ${target.name}`);
}
@LogClass
class MyClass {
constructor() {
console.log('Instancia creada');
}
}
const instance = new MyClass();
Resultado
Clase decorada: MyClass
Instancia creada
Uso avanzado: Agregar metadatos
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
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
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
Método ejecutado: add, con argumentos: 2,3
Decoradores de propiedades
Permiten personalizar el comportamiento de las propiedades de clase, por ejemplo, agregando validaciones o valores predeterminados.
Ejemplo: Validar propiedades
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
Estos decoradores son útiles para realizar validaciones en los parámetros de métodos.
Ejemplo: Validación de parámetros
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
- 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
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.