Els decoradors són una característica avançada de TypeScript que permeten modificar el comportament de classes, mètodes, propietats i paràmetres. Són una forma de metaprogramació que permet afegir funcionalitats addicionals de manera declarativa.
Què són els Decoradors?
Els decoradors són funcions que s'apliquen a una classe, mètode, propietat o paràmetre per modificar el seu comportament. S'utilitzen principalment en frameworks com Angular per afegir metadades a les classes i els seus membres.
Tipus de Decoradors
- Decoradors de Classe: S'apliquen a la definició d'una classe.
- Decoradors de Mètode: S'apliquen a un mètode d'una classe.
- Decoradors de Propietat: S'apliquen a una propietat d'una classe.
- Decoradors de Paràmetre: S'apliquen a un paràmetre d'un mètode d'una classe.
Exemple de Decorador de Classe
Un decorador de classe és una funció que pren com a argument el constructor de la classe i pot modificar-lo o retornar un nou constructor.
function logClass(target: Function) { console.log(`Class ${target.name} is created`); } @logClass class MyClass { constructor() { console.log('MyClass instance created'); } } const myClassInstance = new MyClass();
Explicació
- Definició del Decorador:
logClass
és una funció que pren el constructor de la classe com a argument. - Aplicació del Decorador:
@logClass
s'aplica a la classeMyClass
. - Efecte del Decorador: Quan es crea una instància de
MyClass
, es registra un missatge a la consola.
Exemple de Decorador de Mètode
Un decorador de mètode és una funció que pren com a arguments el prototip de la classe, el nom del mètode i la descripció de la propietat.
function logMethod(target: any, propertyName: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { console.log(`Method ${propertyName} is called with arguments: ${args}`); return originalMethod.apply(this, args); }; return descriptor; } class MyClass { @logMethod myMethod(arg1: number, arg2: string) { console.log('Executing myMethod'); } } const myClassInstance = new MyClass(); myClassInstance.myMethod(42, 'hello');
Explicació
- Definició del Decorador:
logMethod
és una funció que modifica el descriptor del mètode. - Aplicació del Decorador:
@logMethod
s'aplica al mètodemyMethod
. - Efecte del Decorador: Quan es crida
myMethod
, es registra un missatge a la consola amb els arguments passats.
Exemple de Decorador de Propietat
Un decorador de propietat és una funció que pren com a arguments el prototip de la classe i el nom de la propietat.
function logProperty(target: any, propertyName: string) { let value = target[propertyName]; const getter = () => { console.log(`Get value of ${propertyName}: ${value}`); return value; }; const setter = (newValue: any) => { console.log(`Set value of ${propertyName} to ${newValue}`); value = newValue; }; Object.defineProperty(target, propertyName, { get: getter, set: setter, enumerable: true, configurable: true }); } class MyClass { @logProperty myProperty: string; constructor() { this.myProperty = 'initial value'; } } const myClassInstance = new MyClass(); myClassInstance.myProperty = 'new value'; console.log(myClassInstance.myProperty);
Explicació
- Definició del Decorador:
logProperty
és una funció que defineix getters i setters per a la propietat. - Aplicació del Decorador:
@logProperty
s'aplica a la propietatmyProperty
. - Efecte del Decorador: Quan es llegeix o s'escriu
myProperty
, es registra un missatge a la consola.
Exemple de Decorador de Paràmetre
Un decorador de paràmetre és una funció que pren com a arguments el prototip de la classe, el nom del mètode i la posició del paràmetre.
function logParameter(target: any, propertyName: string, parameterIndex: number) { const metadataKey = `log_${propertyName}_parameters`; if (Array.isArray(target[metadataKey])) { target[metadataKey].push(parameterIndex); } else { target[metadataKey] = [parameterIndex]; } } class MyClass { myMethod(@logParameter param1: number, param2: string) { console.log('Executing myMethod'); } } const myClassInstance = new MyClass(); myClassInstance.myMethod(42, 'hello');
Explicació
- Definició del Decorador:
logParameter
és una funció que guarda la posició del paràmetre decorat. - Aplicació del Decorador:
@logParameter
s'aplica al paràmetreparam1
demyMethod
. - Efecte del Decorador: La posició del paràmetre decorat es guarda en una metadada.
Exercicis Pràctics
Exercici 1: Decorador de Classe
Crea un decorador de classe que afegeixi una propietat createdAt
a la classe, que emmagatzemi la data de creació de la instància.
function addCreatedAt(target: Function) { target.prototype.createdAt = new Date(); } @addCreatedAt class MyClass { constructor() { console.log('MyClass instance created'); } } const myClassInstance = new MyClass(); console.log(myClassInstance.createdAt);
Exercici 2: Decorador de Mètode
Crea un decorador de mètode que mesuri el temps d'execució del mètode i el registri a la consola.
function measureExecutionTime(target: any, propertyName: 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(`Execution time of ${propertyName}: ${end - start}ms`); return result; }; return descriptor; } class MyClass { @measureExecutionTime myMethod() { for (let i = 0; i < 1000000; i++) {} // Simulació de treball } } const myClassInstance = new MyClass(); myClassInstance.myMethod();
Exercici 3: Decorador de Propietat
Crea un decorador de propietat que faci que la propietat sigui de només lectura.
function readonly(target: any, propertyName: string) { Object.defineProperty(target, propertyName, { writable: false }); } class MyClass { @readonly myProperty: string = 'initial value'; } const myClassInstance = new MyClass(); myClassInstance.myProperty = 'new value'; // Això hauria de fallar console.log(myClassInstance.myProperty);
Solucions
Solució Exercici 1
function addCreatedAt(target: Function) { target.prototype.createdAt = new Date(); } @addCreatedAt class MyClass { constructor() { console.log('MyClass instance created'); } } const myClassInstance = new MyClass(); console.log(myClassInstance.createdAt);
Solució Exercici 2
function measureExecutionTime(target: any, propertyName: 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(`Execution time of ${propertyName}: ${end - start}ms`); return result; }; return descriptor; } class MyClass { @measureExecutionTime myMethod() { for (let i = 0; i < 1000000; i++) {} // Simulació de treball } } const myClassInstance = new MyClass(); myClassInstance.myMethod();
Solució Exercici 3
function readonly(target: any, propertyName: string) { Object.defineProperty(target, propertyName, { writable: false }); } class MyClass { @readonly myProperty: string = 'initial value'; } const myClassInstance = new MyClass(); myClassInstance.myProperty = 'new value'; // Això hauria de fallar console.log(myClassInstance.myProperty);
Conclusió
Els decoradors són una eina poderosa en TypeScript que permeten modificar el comportament de classes, mètodes, propietats i paràmetres de manera declarativa. Són especialment útils en el desenvolupament de frameworks i biblioteques, on es necessita afegir metadades o modificar el comportament de les entitats de manera consistent. Amb els exemples i exercicis proporcionats, hauríeu de tenir una bona comprensió de com crear i utilitzar decoradors en els vostres projectes TypeScript.
Curs de TypeScript
Mòdul 1: Introducció a TypeScript
- Què és TypeScript?
- Configuració de l'entorn de TypeScript
- Tipus bàsics
- Anotacions de tipus
- Compilació de TypeScript
Mòdul 2: Treballant amb Tipus
Mòdul 3: Tipus Avançats
Mòdul 4: Funcions i Mòduls
- Tipus de Funció
- Paràmetres Opcional i per Defecte
- Paràmetres Rest
- Mòduls i Espais de Noms
- Decoradors
Mòdul 5: Programació Asíncrona
Mòdul 6: Eines i Millors Pràctiques
- Linting i Formatació
- Proves de Codi TypeScript
- TypeScript amb Webpack
- TypeScript amb React
- Millors Pràctiques