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
Decorators for Stage 3 2022-03
By pzuraq
Decorators for Stage 3 2022-03
- 985