Tengo la tarea de implementar un decorador de métodos que permita ejecutar un método decorado solo una vez.
Por ejemplo:
class Test { data: any; @once setData(newData: any) { this.newData = newData; } } const test = new Test(); test.setData([1,2,3]); console.log(test.data); // [1,2,3] test.setData('new string'); console.log(test.data); // [1,2,3]Probé muchas combinaciones para hacer que una función que se llama dos veces no haga nada, pero no es lo que debería tener y las pruebas unitarias están fallando, así que esto es lo que tengo hasta ahora:
const once = ( target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor ) => { const method = descriptor.value; descriptor.value = function (...args){ // ??? } };Pruebas unitarias:
describe('once', () => { it('should call method once with single argument', () => { class Test { data: string; @once setData(newData: string) { this.data = newData; } } const test = new Test(); test.setData('first string'); test.setData('second string'); assert.strictEqual(test.data, 'first string') }); it('should call method once with multiple arguments', () => { class Test { user: {name: string, age: number}; @once setUser(name: string, age: number) { this.user = {name, age}; } } const test = new Test(); test.setUser('John',22); test.setUser('Bill',34); assert.deepStrictEqual(test.user, {name: 'John', age: 22}) }); it('should return always return first execution result', () => { class Test { @once sayHello(name: string) { return `Hello ${name}!`; } } const test = new Test(); test.sayHello('John'); test.sayHello('Mark'); assert.strictEqual(test.sayHello('new name'), 'Hello John!') }) });¿Usted me podría ayudar por favor? ¡Gracias por adelantado!
Prueba algo como esto:
const once = ( target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor ) => { const method = descriptor.value; let isFirstTime = true; descriptor.value = function (...args: any[]) { if (!isFirstTime) { return; } isFirstTime = false; method(...args); } };reflect-metadata es bastante útil para este escenario. Podrías intentar algo como esto:
import 'reflect-metadata'; const metadataKey = Symbol('initialized'); export function once( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const method = descriptor.value; descriptor.value = function(...args) { const initialized = Reflect.getMetadata(metadataKey, target, propertyKey); if (initialized) { return; } Reflect.defineMetadata(metadataKey, true, target, propertyKey); method.apply(this, args); } }Puede encontrar más información aquí: https://www.typescriptlang.org/docs/handbook/decorators.html#metadata