Implementación de Decoradores de Clase en TypeScript y Angular
Resumen
Los decoradores en TypeScript representan una poderosa herramienta para extender la funcionalidad de clases, métodos, propiedades y parámetros. Esta característica, aunque experimental, ofrece una forma elegante de transferir metadatos y añadir comportamientos adicionales a elementos existentes, siendo especialmente útil en frameworks como Angular donde la comunicación entre componentes es fundamental.
¿Qué son los decoradores en TypeScript?
Los decoradores son un patrón de diseño que permite añadir funcionalidades adicionales a elementos existentes sin modificar su estructura original. En TypeScript, se identifican fácilmente por el símbolo @ seguido del nombre del decorador.
Estos pueden implementarse en:
Clases
Métodos
Propiedades
Parámetros
La principal ventaja de los decoradores es que permiten transferir metadatos entre diferentes partes de tu aplicación, facilitando la comunicación entre componentes, especialmente en frameworks como Angular.
¿Cómo implementar un decorador de clase?
Para implementar un decorador de clase, necesitamos seguir estos pasos:
Crear una función que servirá como implementación del decorador.
Aplicar el decorador a la clase usando la sintaxis @nombreDecorador.
Configurar el proyecto para soportar decoradores.
Veamos un ejemplo práctico:
// Implementación del decoradorfunctionpersonaDeck(target:Function){console.log(target);// Extendemos la funcionalidad añadiendo un nuevo método target.prototype.despedir=function(despedida:string):string{return despedida +" "+this.nombre;};}// Aplicación del decorador a la clase@personaDeckclassPersona2{ nombre:string; edad:number;// Declaramos el método que será añadido por el decorador despedir!:(despedida:string)=>string;constructor(nombre:string, edad:number){this.nombre= nombre;this.edad= edad;}saludar(saludo:string):string{return`${saludo}, mi nombre es ${this.nombre} y tengo ${this.edad} años`;}}
Es importante notar que la función del decorador debe definirse antes de usarla con la sintaxis @. De lo contrario, obtendremos un error en tiempo de ejecución.
¿Cómo habilitar los decoradores en un proyecto TypeScript?
Para utilizar decoradores en TypeScript, debemos habilitar la opción experimental en nuestro archivo de configuración tsconfig.json:
{"compilerOptions":{"experimentalDecorators":true,// otras opciones...}}
Puedes crear este archivo ejecutando el comando tsc --init en la terminal dentro de tu proyecto.
¿Por qué usar decoradores en lugar de otras técnicas?
Aunque existen otras formas de extender la funcionalidad de clases (como la herencia o las interfaces), los decoradores ofrecen ventajas únicas:
Metadatos accesibles: Los metadatos pueden ser accedidos desde cualquier parte del proyecto, incluyendo archivos HTML.
Comunicación eficiente: Facilitan la interacción entre diferentes componentes.
Separación de responsabilidades: Permiten añadir funcionalidades sin modificar el código original.
Integración con frameworks: Son especialmente útiles en frameworks como Angular, donde los decoradores como @Component son fundamentales.
Ejemplo práctico en Angular
En Angular, los decoradores son una parte esencial del framework. Por ejemplo, el decorador @Component permite definir metadatos para un componente:
@Component({ selector:'app-root', templateUrl:'./app.component.html', styleUrls:['./app.component.css']})exportclassAppComponent{ title ='Mi Aplicación Angular';}
Este decorador establece la comunicación entre la clase TypeScript y su correspondiente plantilla HTML, facilitando el intercambio de datos y comportamientos.
Los decoradores en TypeScript pueden parecer complejos al principio, pero una vez que comprendes su funcionamiento, se convierten en una herramienta invaluable para crear código más limpio, modular y fácil de mantener. ¿Has utilizado decoradores en tus proyectos? Comparte tu experiencia en los comentarios y descubre cómo esta característica puede mejorar tu desarrollo con TypeScript.
Implementación de Decoradores de Clase en TypeScript y Angular
muy mal estructurado esta parte final del curso para poder entender estos conceptos, ademas lo que siempre digo, utilizan ejemplos que no son reales ni practics, es mas facil aprender un concepto viendo como se usa realmente.
muy mal, esta mucho mejor estructurado y entendible el curso de Nicolas.
Todos los cursos que "actualizan" los desmejoran, la verdad encuentro mas valor hasta en youtube
es verdad. A mi también me costó mucho entender que son y porque usaría decoradores
Concuerdo, para realmente aprender el manejo de cada uno de los conceptos y herramientas sería más útil por aprendizaje que fuera en un ambiente más acercado a la realidad, para saber cuando utilizarlo.
Lo mismo ocurre con la clase de los "types" Son ejemplos que no ayudan a saber cuando utilizarlos realmente
No creo haber escuchado en clases anteriores sobre los prototype este termino puede llegar a confunder a algunas personas sin ese contexto.
Espero 2 cosas haberme equivocado con mi comentario y si no que el enlace le sirva si no conocen acerca de los proptotype en JavaScript
Sin mencionar la parte de Angular, si llego a mencionar, pero acá veo que salió de la nada
Muy probablemente esta clase (por error) se colocó entre las primeras. También me pareció lo mismo. Pero ahora ya la ubicaron entre las últimas siguiendo el orden correcto.
Decorators are one of the most distinctive features of TypeScript, especially if you work with frameworks such as Angular or NestJS. They are a form of metaprogramming that allows you to add behavior, metadata, or transform classes, methods, properties, and parameters at definition time.
In TypeScript, there are two versions of decorators: Legacy/Experimental (enabled with experimentalDecorators: true in tsconfig.json) and Stage 3/TC39 Standard (recently introduced). Here we will look at the modern standard, which is the future of the language.
How decorators work
A decorator is simply a function that is executed when the class is defined (not when it is instantiated). Depending on where it is applied, the function receives different arguments.
1. Class Decorators
These are used to observe, modify, or replace a class definition.
functionLogger(value:Function, context:ClassDecoratorContext){console.log(`Class ${context.name} was defined!`);}@LoggerclassUserService{constructor(){console.log("UserService instance created.");}}
2. Method decorators
They allow you to intercept method calls, which is useful for logging, validation, or measuring execution times.
functionLogMethod(target:any, context:ClassMethodDecoratorContext){const methodName =String(context.name);returnfunction(this:any,...args:any[]){console.log(`Calling ${methodName} with args:`, args);const result = target.apply(this, args);return result;};}classCalculator{@LogMethodadd(a:number, b:number){return a + b;}}const calc =newCalculator();calc.add(5,10);// Log: Calling add with args: [5, 10]
Real world examples
functionRequired(value:undefined, context:ClassFieldDecoratorContext){returnfunction(initialValue:any){if(initialValue ===undefined|| initialValue ===null){thrownewError(`${String(context.name)} is required!`);}return initialValue;};}classProduct{@Required title:string;constructor(title:string){this.title= title;}}// const p = new Product(null as any); // Throws Error: title is required!
Decorator factories (passing arguments)
Sometimes you need to pass data to the decorator (such as @Component({ selector: 'app-root' }) in Angular). For this, we use a Factory: a function that returns a decorator.
Si alguien está utilizando Bun como lo estoy haciendo yo, puede que se le presente un problema al definir el decorador como lo explica el profe. Para corregir el error, el código es el siguiente:
functionPersonaDec<Textends{new(...args:any[]):{}}>(constructor:T){console.log(constructor);returnclassextends constructor {despedir(despedida:string):string{return`${despedida}, ${(thisasany).nombre}`;}};}@PersonaDecclassPersona{ nombre:string; edad:number;constructor(nombre:string, edad:number){this.nombre= nombre;this.edad= edad;}saludar(saludo:string):string{return`${saludo}, mi nombre es ${this.nombre} y tengo ${this.edad} años`;}despedir(despedida:string):string{return`${despedida}, ${this.nombre}`;}}```function PersonaDec\<T extends { new (...args: any\[]): {} }>(constructor: T) { console.log(constructor); return class extends constructor { despedir(despedida: string): string { return `${despedida}, ${(*this*asany).nombre}`; } };}
@PersonaDecclass Persona { nombre: string; edad: number;
  constructor(nombre: string, edad: number) { *this*.nombre = nombre; *this*.edad = edad; }
  saludar(saludo: string): string { return `${saludo}, mi nombre es ${*this*.nombre} y tengo ${*this*.edad} años`; } despedir(despedida: string): string { return `${despedida}, ${*this*.nombre}`;}}
Un decorador en JavaScript no es una característica nativa como en TypeScript, pero puedes simular su comportamiento utilizando funciones. Aquí hay un ejemplo simple:
functionlogger(target){const originalMethod = target.prototype.sayHello; target.prototype.sayHello=function(){console.log('Antes de llamar a sayHello'); originalMethod.apply(this);console.log('Después de llamar a sayHello');};}
@logger
classPerson{sayHello(){console.log('Hola, soy una persona');}}const person =newPerson();person.sayHello();
En este caso, logger actúa como un decorador que envuelve el método sayHello de la clase Person. Aunque esta sintaxis de decoradores no es parte del estándar de JavaScript, puedes utilizar funciones de orden superior para lograr un efecto similar.
Un decorador en TypeScript es una función que permite agregar metadatos o modificar el comportamiento de clases, métodos, propiedades o parámetros. Se utiliza con la sintaxis del símbolo @ seguido del nombre del decorador. Los decoradores facilitan la extensión de la funcionalidad de los elementos y son ampliamente utilizados en frameworks como Angular. Por ejemplo, puedes usar un decorador para agregar un método a una clase o para definir un comportamiento especial en un componente.
Para usar decoradores, asegúrate de que la opción experimentalDecorators esté habilitada en tu tsconfig.json.
De este tema, me confundió mucho que era el target:Functioncomo parámetro de la función del decorador pero con esto me quedó claro:
La cantidad y tipo de parámetros que recibe una función decoradora depende de lo que estás decorando (clase, método, propiedad, etc.).
Según eso, estos son los tipos de decoradores y los parámetros que reciben:
Por ejemplo, esto es lo que significa un decorador de método:
functionDecoradorMetodo(target: any,// El prototipo de la clasepropertyKey: string,// El nombre del método (por ejemplo, 'despedir')descriptor:PropertyDescriptor// Info sobre ese método){...}
¿Y qué es PropertyDescriptor?
Es un objeto que describe cómo se comporta una propiedad (ya sea clase, método, etc.), y tiene estas claves opcionales:
interfacePropertyDescriptor{ value?: any;// el valor real (por ejemplo, el método despedir) writable?: boolean;// si se puede sobrescribir enumerable?: boolean;// si se puede recorrer configurable?: boolean;// si se puede reconfigurar o borrar}
Hace meses ya me había visto los cursos que dio el profe Juan sobre POO en JS (el básico y el intermedio), y creo que eso me ayudó a entender más rápido este tema de los decoradores.
Entonces un decorador sería como un proxy o un middleware? Me llegué a confundir ahí.