2020-09
function readonly(prototype, key, desc) {
return {
...desc,
writable: false,
};
}
class Foo {
@deprecate bar = 123;
}
class Foo {
get foo() {
console.warn('"foo" is deprecated');
return this._foo;
}
set foo(val) {
console.warn('"foo" is deprecated');
this._foo = val;
}
constructor() {
this.foo = 123;
}
}
class Foo {
@deprecate bar = 123;
}
// Field initializer has to be hoisted
function initializer() {
return 123;
}
class Foo {
get foo() {
console.warn('"foo" is deprecated');
if (!('_foo' in this)) {
this._foo = initializer.call(this);
}
return this._foo;
}
set foo(val) {
console.warn('"foo" is deprecated');
this._foo = val;
}
}
// Create a bound version of the method as a field
function bound(elementDescriptor) {
let { kind, key, method, enumerable, configurable, writable } = elementDescriptor;
assert(kind == "method");
function initialize() {
return method.bind(this);
}
// Return both the original method and a bound function field that calls the method.
// (That way the original method will still exist on the prototype, avoiding
// confusing side-effects.)
return {
...elementDescriptor,
extras: [
{
kind: "field",
key,
placement: "own",
enumerable,
configurable,
writable,
initialize
}
]
}
}
class Foo {
@removeAndDuplicate(9) bar = 123;
}
class Foo {
bar1 = 123;
bar2 = 123;
bar3 = 123;
bar4 = 123;
bar5 = 123;
bar6 = 123;
bar7 = 123;
bar8 = 123;
bar9 = 123;
}
export decorator @tracked {
@initialize((instance, name, value) => {
instance[`__internal_${name}`] = value;
})
@register((target, name) => {
Object.defineProperty(target, name, {
get() { return this[`__internal_${name}`]; },
set() { this[`__internal_${name}`] = value; this.render(); },
configurable: true
});
})
}
New Proposal Semantics
function decorate() {
// ...
}
// Applies to all existing decorators
@decorate
class Example {
@decorate myField = 123;
@decorate myMethod() {
// ...
}
@decorate get myAccessor() {
// ...
}
}
// And many potential future ones...
@decorate
function example(@decorate param) {
// ...
}
let @decorate myVar = 123;
@defineElement('my-class')
class MyClass extends HTMLElement { }
function defineElement(name, options) {
return (klass, context) => {
customElements.define(
name,
klass,
options
);
return klass;
}
}
@defineElement('my-class')
class MyClass extends HTMLElement { }
function defineElement(name, options) {
return (klass, context) => {
customElements.define(
name,
klass,
options
);
return klass;
}
}
class MyClass extends HTMLElement { }
MyClass = defineElement('my-class')(
MyClass,
{kind: "class"}
);
function defineElement(name, options) {
// ...
}
@defineElement('my-class')
class MyClass extends HTMLElement { }
function defineElement(name, options) {
return (klass, context) => {
customElements.define(
name,
klass,
options
);
return klass;
}
}
class MyClass extends HTMLElement { }
MyClass = defineElement('my-class')(
MyClass,
{kind: "class"}
);
function defineElement(name, options) {
// ...
}
class MyClass extends HTMLElement { }
MyClass = defineElement('my-class')(
MyClass,
{kind: "class"}
);
function defineElement(name, options) {
// ...
}
class C {
@logged
m(arg) { }
}
function logged(f) {
const name = f.name;
return function(...args) {
console.log(`starting ${name}`);
const ret = f.call(this, ...args);
console.log(`ending ${name}`);
return ret;
}
}
new C().m(1);
// starting m
// ending m
class C {
@logged
m(arg) { }
}
function logged(f) {
const name = f.name;
return function(...args) {
console.log(`starting ${name}`);
const ret = f.call(this, ...args);
console.log(`ending ${name}`);
return ret;
}
}
new C().m(1);
// starting m
// ending m
class C {
m(arg) { }
}
C.prototype.m = logged(
C.prototype.m,
{
kind: "method",
key: "m",
isStatic: false
}
);
function logged(f) {
// ...
}
// class decorator solution
@withBound
class C {
#x = 1;
@bound method() { return this.#x; }
}
// @init: solution
class C {
#x = 1;
@init: bound method() { return this.#x; }
}
function bound(method, {kind, name}) {
assert(kind === "init-method");
return {
method,
initialize() {
this[name] = this[name].bind(this);
}
};
}
class C {
@logged
set x(value) { }
}
function logged(f) {
const name = f.name;
return function(...args) {
console.log(`starting ${name}`);
const ret = f.call(this, ...args);
console.log(`ending ${name}`);
return ret;
}
}
class C {
@logged
set x(value) { }
}
function logged(f) {
const name = f.name;
return function(...args) {
console.log(`starting ${name}`);
const ret = f.call(this, ...args);
console.log(`ending ${name}`);
return ret;
}
}
class C {
set x(value) { }
}
let { set } = Object.getOwnPropertyDescriptor(
C.prototype, "x"
);
set = logged(
set,
{ kind: "setter", isStatic: false }
);
Object.defineProperty(C.prototype, "x", { set });
function logged(f) {
// ...
}
class Element {
@tracked counter = 0;
increment() { this.counter++; }
render() { console.log(counter); }
}
function tracked({get, set}) {
return {
get,
set(value) {
if (get.call(this) !== value) {
set.call(this, value);
this.render();
}
}
};
}
const e = new Element();
e.increment(); // logs 1
e.increment(); // logs 2
class Element {
@tracked counter = 0;
increment() { this.counter++; }
render() { console.log(counter); }
}
function tracked({get, set}) {
return {
get,
set(value) {
if (get.call(this) !== value) {
set.call(this, value);
this.render();
}
}
};
}
const e = new Element();
e.increment(); // logs 1
e.increment(); // logs 2
class Element {
#counter = initialize(0);
get counter() { return this.#counter; }
set counter(v) { this.#counter = v; }
increment() { this.counter++; }
render() { console.log(counter); }
}
let originalDesc = Object.getOwnPropertyDescriptor(
Element.prototype, "counter"
);
let { get, set, initialize = v => v } = tracked(
originalDesc,
{ kind: "field", name: "counter", isStatic: false }
);
Object.defineProperty(
Element.prototype,
"counter",
{ get, set }
);
function tracked() {
//...
}
class Element {
@tracked counter = 0;
increment() { this.counter++; }
render() { console.log(counter); }
}
const IS_TRACKED = Symbol();
function tracked({get, set}, context) {
context.metadata = IS_TRACKED;
return {
get,
set(value) {
if (get.call(this) !== value) {
set.call(this, value);
this.render();
}
}
};
}
function isTracked(C, key) {
return C[Symbol.metadata][key] === IS_TRACKED;
}