← TypeScript EspañolChapter 11 of 12

Decoradores

## Objetivos de Aprendizaje - Comprender el concepto de decorador - Usar decoradores de clase - Aplicar decoradores de metodo - Trabajar con decoradores de propiedad - Configurar TypeScript para decoradores ## Descripcion General Los decoradores son una caracteristica experimental (estadio 2) que permiten modificar clases y sus miembros. Proporcionan una forma de agregar metadatos o cambiar comportamiento. ## Habilitar Decoradores ### tsconfig.json ```json { "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } } ``` ## Decoradores de Clase ### Sintaxis Basica ```typescript function sealed(constructor: Function) { Object.seal(constructor); Object.seal(constructor.prototype); } @sealed class Person { name: string; constructor(name: string) { this.name = name; } } ``` ### Fabricas de Decoradores ```typescript function color(value: string) { return function (constructor: Function) { constructor.prototype.color = value; }; } @color("blue") class Car { brand: string; } console.log((new Car() as any).color); // "blue" ``` ## Decoradores de Metodo ### Decoradores de Accesor ```typescript function readonly( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { descriptor.writable = false; } class Person { @readonly name(): string { return "Alice"; } } let p = new Person(); // p.name = () => "Bob"; // Error: No se puede asignar ``` ### Decorador de Metodo ```typescript function log( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const original = descriptor.value; descriptor.value = function (...args: any[]) { console.log(`Llamando ${propertyKey} con`, args); return original.apply(this, args); }; } class Calculator { @log add(a: number, b: number): number { return a + b; } } let calc = new Calculator(); calc.add(2, 3); // Llamando add con [2, 3] // 5 ``` ## Decoradores de Propiedad ```typescript function defaultValue(value: any) { return function ( target: any, propertyKey: string ) { target[propertyKey] = value; }; } class Config { @defaultValue("localhost") host: string; } let config = new Config(); console.log(config.host); // "localhost" ``` ## Decoradores de Parametro ```typescript function required( target: any, propertyKey: string | symbol, parameterIndex: number ) { // Marcar parametro como requerido } function validate( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const original = descriptor.value; descriptor.value = function (...args: any[]) { // Validar parametros requeridos return original.apply(this, args); }; } class UserService { create( @required name: string, @required age: number ): User { return { name, age }; } } ``` ## Composicion de Decoradores ### Multiples Decoradores ```typescript function first() { console.log("first(): evaluado"); return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { console.log("first(): llamado"); }; } function second() { console.log("second(): evaluado"); return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { console.log("second(): llamado"); }; } class Example { @first() @second() method() {} } // Salida: // first(): evaluado // second(): evaluado // second(): llamado // first(): llamado ``` ## Decoradores de Metodos de Clase ### Decorador de Registro ```typescript function logExecution(label: string) { return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const original = descriptor.value; descriptor.value = function (...args: any[]) { const start = Date.now(); const result = original.apply(this, args); const duration = Date.now() - start; console.log(`${label} tomo ${duration}ms`); return result; }; }; } class UserService { @logExecution("getUser") getUser(id: number) { // operacion costosa return { id, name: "Alice" }; } } ``` ## Descriptor de Propiedad ### Propiedades Disponibles ```typescript { value?: any, writable?: boolean, enumerable?: boolean, configurable?: boolean, get?: () => any, set?: (v: any) => void } ``` ## Reflect Metadata ### Instalacion ```bash npm install reflect-metadata ``` ### Ejemplo ```typescript import "reflect-metadata"; function required( target: Object, propertyKey: string, parameterIndex: number ) { const requiredParams: number[] = Reflect.getMetadata("required", target, propertyKey) || []; requiredParams.push(parameterIndex); Reflect.defineMetadata( "required", requiredParams, target, propertyKey ); } class Person { greet(@required name: string): string { return `Hola, ${name}`; } } ``` ## Casos de Uso Comunes ### Patron Singleton ```typescript function singleton( constructor: T ) { let instance: T; return class extends constructor { constructor(...args: any[]) { if (instance) { return instance; } instance = this as T; super(...args); } }; } @singleton class Database { constructor() { console.log("Base de datos conectada"); } } let db1 = new Database(); let db2 = new Database(); console.log(db1 === db2); // true ``` ### Auto-bind ```typescript function autoBind( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const original = descriptor.value; return { configurable: true, enumerable: false, get() { return original.bind(this); } }; } class Component { @autoBind render() { console.log(this); } } ``` ## Limitaciones ### Experimental - No es parte oficial de ECMAScript aun - La API puede cambiar - Usar con cuidado en produccion ### Orden de Ejecucion - Decoradores evaluados de arriba a abajo - Decoradores llamados de abajo a arriba - Tener cuidado al combinar multiples decoradores ## Resumen - Los decoradores son experimentales en TypeScript - Habilitar con `experimentalDecorators` - Los decoradores de clase reciben constructor - Los decoradores de metodo/accesor reciben descriptor - Los decoradores de propiedad reciben prototipo - Los decoradores de parametro reciben indice - Las fabricas de decoradores retornan funciones de decorador - Multiples decoradores se componen con orden de evaluacion/llamada

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →