TypeScript has been working on an implementation of the Stage 3 decorators proposal, and have been evaluating a number of user scenarios leveraging the current specification text. As part of this, we have found that the current design for the context.access object's API has a somewhat poor DX, and is missing a necessary capability (access.has).
context.access.get and context.access.set DX Concerns
The API for the get and set methods on context.access currently require you to pass the this context via .call:
function dec(target, context) {
context.addInitializer(function() {
// get a value
context.access.get.call(this);
// set a value
context.access.set.call(this, x);
});
}
I believe this was intended to mimic the behavior for method/auto-accessor replacement, i.e.:
function methodDec(target, context) {
return function (...args) {
return target.call(this, ...args);
};
}
function accessorDec(target, context) {
return {
get() { return target.get.call(this); },
set(value) { target.set.call(this, value); },
}
}
However, this may be the wrong behavior to mimic. The .call behavior is a necessity for method replacement, since both the function you are replacing and the function you replace it with must pass along this as the this-binding. However, access.get and access.set don't have that requirement, and more closely resemble Reflect.get/Reflect.set and WeakMap.prototype.get/WeakMap.prototype.set.
We have found the current metaphor, emulating replacement, is far more confusing.
context.access.has Method
Another issue we've found is that, while context.access.get and .set are extremely valuable for some scenarios where private member access is required, the fact that there is no imperative mechanism to emulate #x in obj via context.access can make it difficult for some of these scenarios to perform pre-emptive validation.
Having such an API would also more strongly inform the correct metaphor for .get and .set, in that you are far less likely to expect to do context.access.has.call(obj), given that there is no direct corollary to that in the method replacement metaphor.
Suggested Changes
As such, we would suggest the following changes to the context.access API to improve the development experience:
- Change
context.access.get.call(target) to context.access.get(target)
- Change
context.access.set.call(target, value) to context.access.set(target, value)
- Add
context.access.has(target)
Or, in TypeScript parlance:
type DecoratorContext = {
kind: string;
name: string | symbol;
static: boolean;
private: boolean;
access: {
- get(): any;
- set(value): void;
+ get(target): any;
+ set(target, value): void;
+ has(target): boolean;
},
addInitializer(cb: Function): void;
}
TypeScript has been working on an implementation of the Stage 3 decorators proposal, and have been evaluating a number of user scenarios leveraging the current specification text. As part of this, we have found that the current design for the
context.accessobject's API has a somewhat poor DX, and is missing a necessary capability (access.has).context.access.getandcontext.access.setDX ConcernsThe API for the
getandsetmethods oncontext.accesscurrently require you to pass thethiscontext via.call:I believe this was intended to mimic the behavior for method/auto-accessor replacement, i.e.:
However, this may be the wrong behavior to mimic. The
.callbehavior is a necessity for method replacement, since both the function you are replacing and the function you replace it with must pass alongthisas thethis-binding. However,access.getandaccess.setdon't have that requirement, and more closely resembleReflect.get/Reflect.setandWeakMap.prototype.get/WeakMap.prototype.set.We have found the current metaphor, emulating replacement, is far more confusing.
context.access.hasMethodAnother issue we've found is that, while
context.access.getand.setare extremely valuable for some scenarios where private member access is required, the fact that there is no imperative mechanism to emulate#x in objviacontext.accesscan make it difficult for some of these scenarios to perform pre-emptive validation.Having such an API would also more strongly inform the correct metaphor for
.getand.set, in that you are far less likely to expect to docontext.access.has.call(obj), given that there is no direct corollary to that in the method replacement metaphor.Suggested Changes
As such, we would suggest the following changes to the
context.accessAPI to improve the development experience:context.access.get.call(target)tocontext.access.get(target)context.access.set.call(target, value)tocontext.access.set(target, value)context.access.has(target)Or, in TypeScript parlance:
type DecoratorContext = { kind: string; name: string | symbol; static: boolean; private: boolean; access: { - get(): any; - set(value): void; + get(target): any; + set(target, value): void; + has(target): boolean; }, addInitializer(cb: Function): void; }