proposal-decorators
proposal-decorators copied to clipboard
Decorators for assignment expressions? const/let declarations?
Previous discussion: https://github.com/tc39/proposal-decorators-previous/issues/32
OK to leave as a follow-on proposal?
Use case: runtime guards
This came up at es-discuss "Proposal to add symbol: hasInstanceStrict" that
attempts to provide an intermediate layer for implementing runtime type checking.
to which TJCrowser replied
When [decorators][1] land, provided they land with a means of [decorating functions][2] (either as part of that proposal or as a follow-on), that would do the job, wouldn't it?
The response was that decorators do not allow vetting a value assigned to a local as in
const c: PrimitiveNumber = sum(1, 2);
This issue came up later on:
It seems that there are two separable issues:
- Defining guards
- Syntax for specifying guards.
It seems to me that (2) might be doable with
annotationsdecorators for locals, so deploying guards could build onannotationsdecorators.
Propose to use such syntax:
@primitiveNumber const c = sum(1, 2);
I'd prefer to leave it as a follow on.
I'd like to suggest changing the const decorators extension to include a get. The way it's currently proposed, it's nothing but syntax sugar for a function call, and is inconsistent with the let variant. Changing a variable declaration from let to const shouldn't change the behaviour of reads (provided the decorator supports read-only operation). Such as the example let @deprecated x = 1, should work the same for const @deprecated y = 1.
With a getter on const, you could do lazy evaluation:
const @lazy maybeNeeded = () => calc(...args);
if (day === "Sunday") { result += f(maybeNeeded); }
if (month === "June") { result += g(maybeNeeded); }
function lazy({ get, set, value }, { kind }) {
let ready = false;
switch (kind) {
case "const": return { get: getLazy, value };
case "let": return { get: getLazy, set: setLazy, value };
default: throw Error("bad kind");
}
function getLazy() {
if (!ready) value = get()(), ready = true;
return value;
}
function setLazy(v) {
set(v);
ready = false;
}
}
On another note, can the whole semantics of the { get, set, value } parameter be changed? I don't quite understand the rationale behind allowing the decorator to change the value that's eventually stored in the backing variable. This value travels through decorator chain in the opposite direction than the v in set(v) does, which means you cannot compose decorators that do transformation on assignment.
I would instead suggest { get, set }, where both are single-argument functions returning the transformed value.
// add +1 when reading
function lazyInc({ get, set }) { return { get: v => get(v) + 1, set }; }
// add +1 on initialization and assignment
function eagerInc({ get, set }) { return { get, set: v => set(v + 1) }; }
it's very sad that no decorators for functions have appeared yet.
Considering that classes themselves are syntactic sugar. They could have been decorators.
If you look at the fact that everything in js comes from an object, functions and classes, it becomes funny. Considering that python also has decorators for functions. It's so funny in general
I think all frameworks dream that these decorators would be added. For example React, which moved from classes to functions a long time ago and Angular with pipe.
And if you look at the fact that everyone is now doing meta-frameworks like next.js, where there is both server and client side.
I think these decorators would be indispensable as - for example - middleware.