jayphelps / core-decorators

Library of stage-0 JavaScript decorators (aka ES2016/ES7 decorators but not accurate) inspired by languages that come with built-ins like @​override, @​deprecate, @​autobind, @​mixin and more. Popular with React/Angular, but is framework agnostic.
MIT License
4.52k stars 263 forks source link

@decorateMethod #143

Closed sonhanguyen closed 6 years ago

sonhanguyen commented 6 years ago

wonder what do you think about adding a @decorateMethod to convert method decorators to class decorators? The usecase is implementing the actual decorator pattern. Which using method decorators would look like this:

class ADecorator extends A { // unfortunately Proxy can't be base class
    @log
    @cache
    a(...args) {
        return super.a(...args)
    }

    @log
    b(...args) {
        return super.b(...args)
    }
}

which does look a bit repetitive, I'm proposing the following

@decorateMethod({
   a: [cache, log] // order
   b: log
})
class ADecorator extends A {}

// since ADecorator is empty in this case, it may as well be simplified:
const ADecorator = decorateMethod({
   a: [cache, log]
   b: log
})(A)

looking for some hacktoberfest contribution so just checking if I should open a pull request

jayphelps commented 6 years ago

Shoot, I'm not following 😞 Could you maybe reword with some more real-world examples?

sonhanguyen commented 6 years ago

sorry for the reasoning not being fully fleshed out, my main motivation is that I'm not fan of method decorators. The decorator pattern like many other OOP patterns follow the open/closed principle: given a thing, without modifying the thing's source code, enhance it.

Method decorators require you to do this source code modification, which is sometimes possible sometimes not. e.g. given a 3rd library

export default AbcClient {
   doThing() {
   ...
}

now if I want to apply the decorator @time to doThing. Of course I can't just go into node_modules and annotate the method. I'll instead have to do the following:

import AbcClient as OriginalAbcClient from 'abc'
class AbcClient extends OriginalAbcClient {
   @time
   doThing() {
       return super.doThing(arguments)
   }
}
// rest of the code unchanged 

which is verbose as said, with this hypothetical decorateMethod, I can instead do

const AbcClient = decorateMethod({doThing: time})
    (OriginalAbcClient)
sonhanguyen commented 6 years ago

actually nvm, I just saw we have applyDecorators which is the exact thing