Martin Volek
Martin Volek

Reputation: 1099

Get name of calling property in TypeScript

I want to create a set of generic methods that I would be using instead of backing fields for properties. I need to do some general stuff in the setter (call event emitters in Angular 5) and I don't want to write it every time.

I have tried these three things:

  1. I can pass the property name as a string, but I would like to avoid that.

  2. I also tried to work with Error.stack but the parsing of the stack trace would differ in different browsers and I don't like that.

  3. When I try to use arguments.callee.caller i get: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them.

Any other suggestions?

Example of usage below. Note the // somehow get calling property name comment. In this example I would like there a code that returns "myProperty" string.

class ApplicationComponent {
    private readonly properties = new Map<string, any>();

    protected getProperty<T>(): T {
        const propertyName = // somehow get calling property name
        return <T>this.properties[propertyName];
    }

    protected setProperty<T>(value: T): void {
        const propertyName = // somehow get calling property name
        const oldValue = <T>this.properties[propertyName];

        if (oldValue === value) {
            return;
        }

        this.properties[propertyName] = value;

        const eventEmitter = this[`${propertyName}Change`];
        if (eventEmitter != null && eventEmitter instanceof EventEmitter) {
            eventEmitter.emit(value);
        }
    }
}

class SomeComponent extends ApplicationComponent {
    get myProperty(): SomeType {
        return this.getProperty();
    }

    set myProperty(value: SomeType) {
        this.setProperty(value);
    }

    readonly myPropertyChange = new EventEmitter<SomeType>();
}

Upvotes: 2

Views: 2069

Answers (1)

Estus Flask
Estus Flask

Reputation: 222319

Verbosity helps, because it's impossible to reliably get property name in such cases in production. If getProperty and setProperty should affect a particular property, they should accept property name as a parameter.

This is a good use case for property decorators. A decorator accepts class prototype object and property name. It can then set property descriptor to whatever it needs:

function property(proto, key) {
  Object.defineProperty(proto, key, {
    get() {
      return this.properties[key]
    },
    set(val) { ... }
  });
}

class SomeComponent extends ApplicationComponent {
  @property
  myProperty: SomeType;
}

Upvotes: 1

Related Questions