Marco de Wit
Marco de Wit

Reputation: 2804

For instances of reference data, I would like to make the getters self replacing with the return value

My app loads instances of reference data. Some of the instances have getters that use data from other reference data. Like this:

class Person{
  name: string 
  cityId: number

  get city(): City {
    return City.all.get(this.cityId)!
  }
}

As the reference data never changes, a call to a getter will always return the same result. So I would like to make a getter that replaces itself with its result. I was thinking of a decorator like this:

export const selfReplacing = <T>(target: T, propertyKey: keyof T, descriptor: PropertyDescriptor): any => {
  const oldGetter = descriptor.get!
  descriptor.get = function (this: T) {
    const value = oldGetter.call(this)
    Object.defineProperty(this, propertyKey, {
      value,
      enumerable: true
    })
    return value
  }
}

Which I could use like this:

class Person{
  name: string 
  cityId: number

  @selfReplacing
  get city(): City {
    return City.all.get(this.cityId)!
  }
}

Is this the best way to do it?

Upvotes: 1

Views: 20

Answers (1)

Estus Flask
Estus Flask

Reputation: 222750

Redefining property descriptor on first property access is the best way to do that.

Alternatively, a value can be cached to local variable or, better, a property:

export const selfReplacing = <T>(target: T, propertyKey: keyof T, descriptor: PropertyDescriptor): any => {
  const oldGetter = descriptor.get!
  const cacheKey = Symbol(`${propertyKey} cache`);
  descriptor.get = function (this: T) {
    if (!(cacheKey in this))
      this[cacheKey] = oldGetter.call(this);

    return return this[cacheKey];
  }
}

May be helpful if some caching strategy should be additionally implemented (cache expiration, etc).

Upvotes: 1

Related Questions