Waseem
Waseem

Reputation: 751

ES.next decorators that get called either directly or as a factory function

I have looking at some example of the the ES.next decorators and noticed that it might be possible to make a decorator that gets applied as a factor function that takes arguments, or directly by omitting the () at the end at the same time.

I managed to get either styles to work separately, as a factory function @decorate(withArgs), or directly @decorate, but not both!

Here's an example: https://github.com/jayphelps/core-decorators.js#deprecate-alias-deprecated

class foo {

  @deprecate
  foo() {}

  @deprecate("with an optional string")
  bar() {}
}

I tried to examine the source code mentioned above, but with my limited experience with decorators, I couldn't figure out how to set up something like that.


Here's how I managed to get @decorate to work without using any arguments

function decorate(target, key, descriptor) {
  // do some stuff and then return the new descriptor
}

and here's how I managed to get @decorate(args) to work with arguments as a factory function:

function decorate(...args) {
  return function(target, key, descriptor) {
    // do some stuff using args and then return the new descriptor
  }
}

As you can see, at the point it's either decorate foo() or decorate(args) foo(), not both.

Upvotes: 2

Views: 306

Answers (1)

Dogoku
Dogoku

Reputation: 4675

When writing @decorator the browser expects that the decorator function is called immediately, where as, when writing @decorator(args) it expects that a factory is called first, which will return a decorator function.

Here's an example of a decorator i wrote that adds a state property to a class that is driven by redux

export default function state (defaultState) {

    function reducer(state, action) {
        if (!state) {
            state = defaultState;
        }
        ...
    }

    function decorator (target) {...}

    // If 1st argument is a function, it means `@state` was written
    if (typeof defaultState === 'function') {
        let target = defaultState;
        defaultState = {};
        return decorator(target);
    } else {
        return decorator;
    }
}

Do note, that the decorator in the example is a class decorator, which has a different signature (target) than the method decorator you are writing (target, key, descriptor)

The decorator can be used with or without a parameter

import state from './decorators/redux-state'

@state({
    name: '',
})
class MyClass {
    ...
}

@state
class MyOtherClass {
    constructor(state= {name: ''}) {
        this.state = state;
    }
    ...
}

Jay Phelps, is abstracting the logic of figuring out how the decorator was called in a decorate utility function, and that makis his code harder to follow.

Hope this helps

Upvotes: 3

Related Questions