Decorators, Stage 2 update:�Statically Analyzable
Daniel Ehrenberg
Igalia, in partnership with Bloomberg
March 2019 TC39
Popularity
i.e. roughly, the proposal as of 2014
Excitement
Some goals
History of decorator proposals
In 2014/2015: TypeScript "experimental"/Babel "legacy"
How decorators originally worked
2016: Descriptor-based decorators
interface MemberDesciptor {
kind: "property"
key: string,
isStatic: boolean,
descriptor: PropertyDescriptor,
extras?: MemberDescriptor[]
finisher?: (klass): void;
}
interface ClassDesciptor {
kind: "class",
constructor: Function,
heritage: Object | null,
elements: MemberDescriptor[]
}
2017-2019: Descriptor-based proposal grows and evolves
Language-level issues with decorators
Complex to write decorators
Difficult to extend over time
Ember's experience and issues with the original decorators proposal
Why are descriptor-based decorators slow?
Transpiler implementations
Native implementations
It's nice if it's easier to see what's going on
Decorators tend to have a fixed shape
Built-in decorators and composition
The idea
Built-in decorators
Decorators defined in JavaScript
decorator @foo { @bar @baz @bing }
@register
@register
class C {
@register(f) method() { }
}
class C {
method() { }
}
f(C.prototype, "method");
@register
@register(f)
class C { }
class C {
method() { }
}
f(C);
@defineElement
import { @defineElement } from� "./defineElement.mjs";
@defineElement('my-class')
class MyClass extends HTMLElement {
/* ... */�}
// defineElement.mjs
export decorator @defineElement(� name, options) {
@register(klass =>� customElements.define(� name, klass, options))
}
@wrap
@wrap
class C {
@wrap(f) method() { }
}
class C {
method() { }
}
C.prototype.method =� f(C.prototype.method);
@wrap
@wrap(f)
class C { }
class C { }
C = f(C);
@logged
import { @logged } from "./logged.mjs";
class C {
@logged
method(arg) {
this.#x = arg;
}
@logged
set #x(value) { }
}
new C().method(1);
// starting method with arguments 1
// starting set #x with arguments 1
// ending set #x�// ending method
// logged.mjs
export decorator @logged {
@wrap(f => {
const name = f.name;
function wrapped(...args) {
console.log(`starting ${name} with arguments ${args.join(", ")}`);
f.call(this, ...args);
console.log(`ending ${name}`);
}
wrapped.name = name;
return wrapped;
})�}
@initialize
@initialize
class C {
@initialize(f) a = b;
}
class C { }
C = f(C);
class C {
constructor() {
f(this, "a", b);
}
}
@initialize
@initialize(f)
class C { }
class C {
constructor() {
f(this);
}
}
@bound
import { @bound } from "./bound.mjs";
class Foo {
x = 1;
@bound method() {� console.log(this.x);� }
queueMethod() {� setTimeout(this.method, 1000);� }
}
new Foo().queueMethod();�// logs 1, not undefined
// bound.mjs
export decorator @bound {
@initialize((instance, name) =>� instance[name] =� instance[name].bind(instance))
}
@expose
@expose
class C {
@expose(f) #x;
}
class C {
@register(proto =>� f(proto,
"#x",
instance => instance.#x,
(instance, value) =>� instance.#x = value ))
#x;
}
@show
import { FriendKey, @show } from� "./friend.mjs";�
let key = new FriendKey;
export class Box {
@show(key) #contents;
}
export function setBox(box, contents) {
return key.set(box, "#x", contents);
}
export function getBox(box) {
return key.get(box, "#x");
}
export class FriendKey {
#map = new Map();
expose(name, get, set) {
this.#map.set(name, { get, set });
}
get(obj, name) {
return this.#map.get(name).get(obj);
}
set(obj, name, value) {
return this.#map.get(name)� .set(obj, value);
}
}
export decorator @show(key) {
@expose((target, name, get, set) =>� key.expose(name, get, set))
}
Common features of decorators
Syntax
Semantics/phasing
Potential future built-in decorators
Decorators for other syntactic forms
Built-in support for common scenarios
Error checking
Property descriptor/placement change
Statically change the structure of the class
Implementation notes:�Some complexity, but more optimizable
Could implement this directly
JavaScript compilation becomes non-local
Transpilers: .decorators.json
Custom decorators in tooling
Native JS implementations:�Bytecode based on dependencies
Next steps
Recommendations for authors of decorators today
Prototyping this proposal