Typescript Property Decorator in Vanilla JS?

Viewed 325

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?

2 Answers

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);

Decorators are still a proposal, and have not yet officially been added to JS. They're currently undergoing a major redesign and they will be quite different from the current TypeScript implementation, which is based on the first version of the proposal. You can follow progress on the proposal here: https://github.com/tc39/proposal-decorators

You could also use Babel to try decorators in JS in the meantime, but this is also based on the older proposal (even with the legacy option turned off...since decorators are actually now in a third version). https://babeljs.io/docs/en/babel-plugin-proposal-decorators

So keep in mind that this is only a stopgap measure and that the code probably won't ever work natively in browsers in the future—you'll need to refactor it to match the new decorators proposal whenever it's completed.

Related