Search code examples
javascriptdecorator

Typescript Property Decorator in Vanilla JS?


I found some code that uses a decorator as an instance variable in a class...

I tried to replicate this in plain Javascript, but it doesn't work.

This is the Typescript:

export function ObservableProperty() {
  return (obj: Observable, key: string) => {
    let storedValue = obj[key];

    Object.defineProperty(obj, key, {
      get: function () {
        return storedValue;
      },
      set: function (value) {
        if (storedValue === value) {
          return;
        }
        storedValue = value;
        this.notify({
          eventName: Observable.propertyChangeEvent,
          propertyName: key,
          object: this,
          value
        });
      },
      enumerable: true,
      configurable: true
    });
  };
}

Then, in the class:

export class MyClass extends Observable {
  @ObservableProperty() public theBoolValue: boolean;
…

I tried all sorts of ways to instantiate my JS-variable - always get errors…

Like:

@ObservableProperty
this.theBoolValue = false;

Is this even possible?


Solution

  • Decorators are a TypeScript thing, not a JavaScript thing (yet)

    Looking at the TypeScript Docs, you can take their example (or even your example for that matter), and throw it in their Playground to get a the JavaScript equivalent.

    For example, here is the TypeScript example:

    function first() {
      console.log("first(): factory evaluated");
      return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("first(): called");
      };
    }
    
    function second() {
      console.log("second(): factory evaluated");
      return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("second(): called");
      };
    }
    
    class ExampleClass {
      @first()
      @second()
      method() {}
    }
    

    And that boils down to this in JavaScript:

    "use strict";
    var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    };
    var __metadata = (this && this.__metadata) || function (k, v) {
        if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
    };
    function first() {
        console.log("first(): factory evaluated");
        return function (target, propertyKey, descriptor) {
            console.log("first(): called");
        };
    }
    function second() {
        console.log("second(): factory evaluated");
        return function (target, propertyKey, descriptor) {
            console.log("second(): called");
        };
    }
    class ExampleClass {
        method() { }
    }
    __decorate([
        first(),
        second(),
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], ExampleClass.prototype, "method", null);