tc39/proposal-decorators

Decorators should me "mixins"

saviski opened this issue · 3 comments

Decorator should be implemented as mixins

just like

constructor

@classDecorator class MyClass {}

const classDecorator = (Class) => class extends Class {
  constructor(...args) {
    super(...args)
    // init some stuff
  }
}

method

class MyClass {

  @methodDecorator 
  method() {}
}
const methodDecorator = (Class, method) => class extends Class {
  [method](...args) {
   // can change args
    let result = super[method](...args)
    // can change result
    return result
  }
}

property

class MyClass {

  @propertyDecorator  property
}
const propertyDecorator = (Class, property) => class extends Class {
  get [property]() {
    return 'yeah'
  }

  set[property] {
    // does something
  }
}

parameter

class MyClass {

  method(@argumentDecorator arg) {}
}
const argumentDecorator = (Class, method, index) => class extends Class {
  [method](...args) {
    if (typeof args[index] != 'string') throw 'not a string'
    let result = super[method](...args)
    return result
  }
}

this way any decorator can redefine anything, another method, parameter, define a new constructor with arbitrary initializer code
it just works

@A 
class cls {
  @B prop;
  @C method(@D arg) {}
}

would be converted into

let cls = class cls {
  prop;
  method(arg) {}
}
cls = B(cls, 'prop')
cls = D(cls, 'method', 0)
cls = C(cls, 'method')
cls = A(cls)

for example, there is nothing impeding a property decorator from redefining the constructor or another method

class MyClass {

  @propertyDecorator  property
}
const propertyDecorator = (Class, property) => class extends Class {
 
  constructor(...args) {
    super(...args);
    this[property] = 'init value'
  }

  anotherMethod(...args) {
    super.anotherMethod?.(...args)
    return 'other value'
 }
}

It even makes pipes people happy:

let cls = class cls {
  prop;
  method(arg) {}
}
|> B(#, 'prop')
|> D(#, 'method', 0)
|> C(#, 'method')
|> A(#)

"a decorator can redefine anything" is something that implementors soundly rejected, so this is a nonstarter.

As @ljharb pointed out, this does not meet the constraints that we've been given and have designed for, so I'm going to close this issue.