Search code examples
javascriptdecorator

Javascript use decorator to change static class field value?


Is it possible for a JS field decorator to change its value?

A simplified use case would be something like this:

const addItem = (newValue) => {
  return function (target) {
    target.value.push(newValue);
  };
};

class Test {
  @addItem(4)
  static values = [1,2,3];
}

const test = new Test();
console.log(test.constructor.values) // [1,2,3,4]

Using the following experimental decorators:

  '@babel/plugin-proposal-decorators',
  {
    version: '2018-09',
    decoratorsBeforeExport: true,
  },

End goal is to make a decorator to inject tailwind style sheets into a lit elements static styles. Currently using a mixin for this but just doing this for fun and to learn whats possible with decorators.

Update to Barmars comments

When trying to return a value from the inner function, I end up getting an error:

export const addItem = (value) => {
  return function (target) {
    return [value];
  };
};

Uncaught TypeError: An element descriptor's .kind property must be either "method" or "field", but a decorator created an element descriptor with .kind "undefined"

Looking at the documentation, the variables getting passed to each of these functions doesn't seem to match either.

function logged(value, { kind, name }) {
  if (kind === "field") {
    return function (initialValue) {
      console.log(`initializing ${name} with value ${initialValue}`);
      return initialValue;
    };
  }
}

When running that example, the 2nd parameter to logged() is undefined. "initialValue" also is an object, not the value:

Object { kind: "field", key: "styles", placement: "own", descriptor: {…}, initializer: value(), … }

Solution

  • Nicolo Ribaudo was able to help me over on Babel's discussions. The correct way to do this is to use the initializer function:

    const addItem = (newValue) => {
      return function (target) {
        const { initializer } = target;
        target.initializer = function () {
          return [
            ...initializer.call(this),
            newValue,
          ];
        };
      };
    };
    
    class Test {
      @addItem(4)
      static values = [1,2,3];
    }
    
    const test = new Test();
    console.log(test.constructor.values) // [1,2,3,4]