By Daniel Ehrenberg.
JavaScript decorators were created in 2014 as a collaboration among the JavaScript ecosystem, and you've been able to use them in TypeScript and Babel. But they didn't make it into the JavaScript standard yet: not ES6, or any of the later versions, so far. We're working on standardizing decorators in TC39, the JavaScript standards committee, but some changes are required from the initial version.
In this talk, Daniel will explain what TC39 is and how we work. We'll look at some newer language feature proposals, such as Temporal and immutable records and tuples, and follow how decorators have been proceeding through the TC39 process, including why and how they're changing. TC39 could use your help in moving JavaScript forward.
(c) Full Stack Fest 2019
Sitges, Barcelona
September 4—6, 2019
https://2019.fullstackfest.com/
6. Who is TC39?
● A committee of Ecma, with…
● JS developers
● JavaScript engines
● Transpilers
● Frameworks, libraries
● Academics
● etc
7.
8.
9. Meetings
● Every two months
● For three days
● Summarize/review GitHub activity
● Move proposals through stages
10. TC39 stages
● Stage 1: An idea under discussion
● Stage 2: We're doing this and have a first draft
● Stage 3: Basically final draft; ready to go
● Stage 4: 2+ implementations, tests ⇒ standard
17. var street = user.address && user.address.street;
// ===>
var street = user.address?.street
var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined
// ===>
var fooValue = myForm.querySelector('input[name=foo]')?.value
30. class PrivateCounter {
#x = 0;
}
let p = new PrivateCounter();
console.log(p.#x); // SyntaxError
class PublicCounter {
_x = 0;
}
let c = new PublicCounter();
console.log(c._x); // 0
Stage 3
36. import Component from '@ember/component';
import { computed } from '@ember/object';
import { tagName, className } from
'@ember-decorators/component';
@tagName('a')
export default class ExampleComponent
extends Component {
@className
@computed('someKey', 'otherKey')
get bar() {
return
`.${this.someKey}__${this.otherKey}`;
}
}
import Component from
'@ember/component';
import { computed } from
'@ember/object';
export default Component.extend({
tagName: 'a',
classNameBindings: ['bar']
bar: computed('someKey',
'otherKey',
function() {
return
`.${this.someKey}__${this.otherKey}`;
}),
})
Ember
decorators
37. Popularity
● JS developers are really excited about decorators!
● Used in many frameworks and libraries, e.g.,
○ Angular
○ Ember
○ Salesforce Lightning
○ Polymer/lit-element
○ MobX
● General sentiment: They are used so heavily, are already
there, and should be standardized
38. Use cases reported
● Original capabilities
○ Decorating classes, fields, methods
● Features of the Stage 2 proposal
○ Interaction with private and fields
○ Scheduling callbacks on construction
● Features not yet proposed
○ mixins, functions, let
39. Some goals
● Decorators should be fast in implementations:
○ Transpilers
○ JS engines
● Decorators should be easy to use:
○ Using someone else's decorators
○ Writing your own
46. Why descriptor-based decorators, not the original ones?
Both too dynamic and too limited
● [[Set]] vs [[Define]]
● #private
● Extensible over time, how?
● Replacement of property descriptors
● TypeScript vs Babel details
48. Complex to write decorators
● Need to understand Object.defineProperty deeply
○ Even with original decorators
○ Ecosystem abstraction layers
● With Stage 2 proposal, expanded descriptor language
49. Difficult to extend over time
● Past discussion about "elements"
● Passing new fields back from decorators
● Mixin feature request
51. Transpiler implementations
● Slower in practice -- LinkedIn experiment
● Lots of code generated
● Lots of descriptors to generate and process
● Unclear how to generate good code
52. Native implementations
● Interpreter needs to be fast too
● Generating bytecode similar to static compilers!
● Decorators make many things observable
56. The idea
● Basic building blocks: Built-in decorators
● Compose to make JavaScript-defined decorators
● @decorators are separate, static, lexically scoped
65. @logged
import { @logged } from "./logged.mjs";
class C {
@logged
method(arg) {
/* */
}
}
new C().method(1);
// log
class C {
method(arg) {
console.log("log");
/* */
}
}
new C().method(1);
66. @wrap
class C {
@wrap(f) method() { }
}
class C {
method() { }
}
C.prototype.method =
f(C.prototype.method);
67. Using @wrap directly
class C {
@wrap(f => {
function wrapped(...args) {
console.log("log");
f.call(this, ...args);
}
return wrapped;
})
method(arg) {
/* */
}
}
class C {
method(arg) {
console.log("log");
/* */
}
}
const prev = C.prototype.method;
C.prototype.method = function(...args) {
console.log("log");
f.call(this, ...args);
}
68. Implementing @logged
import { @logged } from "./logged.mjs";
class C {
@logged
method(arg) {
/* */
}
}
new C().method(1);
// log
// logged.mjs
export decorator @logged {
@wrap(f => {
function wrapped(...args) {
console.log("log");
f.call(this, ...args);
}
return wrapped;
})
}
70. Recommendations for authors of decorators today
● Just keep using "legacy"/"experimental" decorators
● Goal of this proposal:
For users (not authors) of decorators,
upgrading to standard should be a codemod
71. Developing consensus
● Not everyone agrees on this version!
● Positive feedback from some
● Concerns raised from others (preferring more dynamic)
● Let's keep discussing!
● TC39 works by consensus
72. Prototyping this proposal: Plan from here
● Continue discussions towards consensus
● Write specification
● Implement these new decorators in Babel
○ Including some "post-MVP" decorators
● Try out the new decorators
● Collect feedback
● Propose for Stage 3 after some months of stability+use
73. I regret that we are in an uncomfortable
intermediate state
82. But also, take it easy!
● No need to follow this stuff to be a good developer!
● We all do different things
● Nobody understands everything
● Important to have balance when contributing
83. Conclusion
● Decorators are in progress!
● They will be different from
what you're using now, but
hopefully better
● You can help TC39 move
JavaScript forward
● Daniel Ehrenberg
● @littledan
● https://tc39.es