Декоратор метода области действия для экземпляра класса

#javascript #typescript #decorator

Вопрос:

Я пытаюсь создать простой кэш с помощью декоратора методов.

Допустим, у меня есть следующий класс:

 class Contract {
  constructor(public readonly contractAddress: string) {}

  @cache()
  async getBalance(): Promise<number> {
    // Get balance for this.contractAddress from a backend and return it
    return balance
  }
}
 

В настоящее время у меня есть следующий декоратор кэша:

 
const promiseCache = new Map<string, Promise<unknown>>()

const cache = () => {
  return (_target: Object, propertyKey: string, descriptor: PropertyDescriptor) => {
    const originalMethod = descriptor.value

    descriptor.value = async function (...args: any[]) {
      const constructedKey = constructKey(args)
      const promise = promiseCache.get(constructedKey)
      if (promise) {
        return promise
      } else {
        const newPromise = originalMethod.apply(this, args)
        promiseCache.set(constructedKey, newPromise)
        return newPromise
      }
    }

    return descriptor
  }
}
 

Это работает нормально, если у меня есть только один экземпляр этого класса. Однако, если я создам второй экземпляр с другим contractAddress , то кэш, по-видимому, будет таким же.

 // Example

const a = new Contract('a') // has balance of 1
const b = new Contract('b') // has balance of 2

await a.getBalance() // returns 1
await b.getBalance() // returns 1
 

Я попытался найти ответ на эту проблему, но единственное, что я обнаружил, — это то, что невозможно передать экземпляр класса декоратору, поэтому я не могу получить доступ contractAddress , например, для добавления его в свой ключ.

Я также попытался поместить «Тайник обещаний» на фабрику декораторов, но это тоже не сработало.

Последнее, что я пробовал, — это добавить параметр на фабрику декораторов, но это тоже не сработало.

 // THIS DOES NOT WORK

const cache = (id: number) => { ... }

@cache(Math.random())
getBalance() { ... }
// This gave me a random number in the decorator, but it seemed to be the same one per method across all instances

// or 

@cache(this.contractAddress) // This doesn't even compile
getBalance() { ... }
 

Is this possible? The only 2 solutions I see are either not using decorators and adding a cache into every method (which I would like to avoid because of the extra boilerplate), or passing in the contractAddress as an argument for every method, which would also not be nice.