Decorators Normative Update: Flexible Initializers

Chris Hewell Garrett

2022-06

Current

Decorators can add initializers with the `addInitializer` method of the context object. These added-initializers run in 1 of 3 phases, depending on the value being decorated.

  • Instance added-initializers run prior to field assignment on every instance 
  • Static added-initializers run prior to static field assignment on the class itself
  • Class added-initializers run immediately after the class is fully defined (e.g. after static fields have been assigned)
@dec
class C {
  foo = 123;
  @dec bar() {}

  static foo = 123;
  @dec static bar() {}
}

// Approximate transpilation

class C {
  foo = (runInstanceInitializers(this), 123);
  bar() {}

  static foo = (runStaticInitializers(this), 123);
  static bar() {}
}
runClassInitializers(C);

Proposed

First parameter to `addInitializer` determines placement. Can be "instance", "static", or "class". Benefits:

  • Provides more flexibility. Instance methods often require some amount of static initialization (ex: props on web-components)
  • Metadata can be added by instance decorators with static/class initializers. No need for Symbol.metadata or additional APIs (though there are still benefits for them, will continue to explore separately)
function dec(value, context) {
  context.addInitializer('instance', () => {
    console.log('instance!') 
  });
  
  context.addInitializer('static', () => {
    console.log('static!') 
  });
  
  context.addInitializer('class', () => {
    console.log('class!') 
  });
}

@dec class C {}
// static! class!

new C();
// instance!