Decorators for Stage 3

Chris Hewell Garrett

2022-03

Refresher: What is a decorator?

Decorators are functions which have four main capabilities when applied to a class or class element

  • Replacement
  • Initialization
  • Metadata
  • Access

Refresher: Replacement

Decorators receive the original value, and can replace it with another value that has the same shape.

  • Methods can be replaced with a new method
  • Initial field values can be replaced with new values
  • Cannot change a field into a method, getter into a setter, etc. Has to be the same.
function wrapMethod(m) {
  return function() {
    console.log('before');
    m.call(this);
    console.log('after');
  }
}

class C {
  @wrapMethod m() {
    console.log('method');
  }
}

(new C).m();
// before
// method
// after

Refresher: Initialization

Decorators can run code to initialize the decorated value per-instance.

  • Method decorators can call `addInitializer`. Method initializers run before class fields are assigned.
  • Field decorators can run initialization code within the wrapped initializer
function bound(m, context) {
  context.addInitializer(function() {
    this[context.name] = 
      this[context.name].bind(this);
  });
}

class C {
  x = 123;
  
  @bound m() {
    console.log(this.x)
  }
}

const { m } = new C();

m(); // 123

Refresher: Metadata

Decorators can add metadata which is then readable by external code.

  • Metadata is defined via `setMetadata`
  • Metadata is keyed on a Symbol
  • Metadata is split between public and private
  • Metadata is inherited
  • Accessible on the class via `Symbol.metadata`
const MY_META = Symbol();

function addMeta(value) {
  return function(m, context) {
    context.setMetadata(MY_META, value);
  } 
}

@addMeta('class')
class C {
  @addMeta('pub') x;
  @addMeta('priv') #x;
}

C.prototype[Symbol.metadata][MY_META].own; 
// 'class'
C.prototype[Symbol.metadata][MY_META].public.x; 
// 'pub'
C.prototype[Symbol.metadata][MY_META].private[0]; 
// 'priv'

Refresher: Access

Decorators can provide access to the decorated value via `access` object

  • `access` object contains a getter that accesses the value if it is gettable (e.g. field, getter, method), and a setter if the value is settable (e.g. field, setter).
const EXPOSE = Symbol();

function expose(value, context) {
  context.setMetadata(EXPOSE, context.access.get);
}

class C {
  @expose x = 123;
  @expose #x = 456;
}

const c = new C();
const meta = C.prototype[Symbol.metadata][EXPOSE];

meta.public.x.call(c); 
// 123
meta.private[0].call(c);
// 456

Refresher: Accessor Keyword

`accessor` can be used to create automatic getters/setters on a fiel

  • These getters/setters can then be decorated
class C {
  @reactive accessor foo = 123;  
}

Updates Since Last Meeting

  • `initialize` on accessor decorator return value renamed to `init`
  • `access` object included for public elements as well as private
  • `constructor` on metadata renamed to `own`
  • Spec has been fully written and updated, and has been reviewed by a number of members.

Asking for advancement to Stage 3