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:
@logClasss'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:
@logMethods'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:
@logPropertys'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:
@logParameters'aplica al paràmetreparam1demyMethod. - 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
