ES7 proposal
The ES7 proposal for decorators can be found here: https://github.com/wycats/javascript-decorators
The ES7 proposal serves as the base of this proposal. Below are notes about how the type systemDecorator targets:
@F("color")
@G
class Foo {
}
desugars to:
var Foo = (function () {
function Foo() {
}
Foo = __decorate([F("color"), G], Foo);
return Foo;
})();
class Foo {
@F(color)
@G
bar() { }
}
desugars to:
var Foo = (function () {
function Foo() {
}
Foo.prototype.bar = function () {
};
Object.defineProperty(Foo.prototype, "bar", __decorate([F(color), G], Foo.prototype, "bar", Object.getOwnPropertyDescriptor(Foo.prototype, "bar")));
return Foo;
})();
class Foo {
@F("color")
@G
static sMethod() {}
}
desugars to:
var Foo = (function () {
function Foo() {
}
Foo.sMethod = function () {
};
Object.defineProperty(Foo, "sMethod", __decorate([F("color"), G], Foo, "sMethod", Object.getOwnPropertyDescriptor(Foo, "sMethod")));
return Foo;
})();
class Foo {
@F("color")
@G
prop: number;
}
desugars to:
var Foo = (function () {
function Foo() {
}
__decorate([F("color"), G], Foo.prototype, "prop");
return Foo;
})();
class Foo {
method(@G a, @F("color") b) {}
}
desugars to:
var Foo = (function () {
function Foo() {
}
Foo.prototype.method = function (a, b) {
};
__decorate([G], Foo.prototype, "method", 0);
__decorate([F("color")], Foo.prototype, "method", 1);
return Foo;
})();
Where the __decorate is defined as:
var __decorate = this.__decorate || function (decorators, target, key, value) {
var kind = typeof (arguments.length == 2 ? value = target : value);
for (var i = decorators.length - 1; i >= 0; --i) {
var decorator = decorators[i];
switch (kind) {
case "function": value = decorator(value) || value; break;
case "number": decorator(target, key, value); break;
case "undefined": decorator(target, key); break;
case "object": value = decorator(target, key, value) || value; break;
}
}
return value;
};
A valid decorator should be:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;
var x = dec(function () { });
Excuse me from what I understand of the spec, we won't be able to do:
@F
function test() {
}
Am I right ?
How does type serialization work with rest arguments?
@F()
class Foo {
constructor(...args: string[]) {
}
}
function F(@paramterTypes types?: Function[]) {
return function (target) {
target.paramterTypes = types; // ???
}
}
Using decorators seems straightforward enough, but I found the sections about declaring them to be confusing. C.4 says decorators need to be annotated with @decorator
, but not a single one of the examples actually shows this happening.
Are decorator factories intended to be classes that implement the interfaces found in B?
What is the rule for refining the interpretation of CoverMemberExpressionSquareBracketsAndComputedPropertyName?
I noticed many of the typings have Function | Object
at various points, but these will degenerate to Object at type check time. What is the reason to have Function there?
I am not crazy about the terms DecoratorFunction vs DecoratorFactory. I'd much rather follow the nomenclature of generators, which has Generator and GeneratorFunction. With this scheme, we would rename DecoratorFunction to Decorator, and DecoratorFactory to DecoratorFunction.
For the decorated exports, what is [lookahead ≠ @]
for? Can HoistableDeclaration and ClassDeclaration actually start with a @
?
This is a dup of #1557
It's not really a dupe, as #1557 was for a different design. This issue is for the decorators design being implemented now.
My mistake.
For decorator on function expression, could we not do something like :
@F("color") @G
function myFunc() {
doSomething();
}
transformed in :
var _t = function() {
doSomething();
}
_t = F("color")(_t = G(_t) || _t) || _t;
function myFunc() {
return _t.apply(this, arguments)
}
It's a bit bother some to have to right every function like :
const myFunc = function () {}
You loose hoisting, and function.name
Implementation added in PR #2399
Updates: Proposal updated, and added link to the @wycats ES7 JavaScript decorators.
saddened that it became a class only thing ...
Also what about ambiant decorator did they get out of the scope ?
@fdecampredon with your proposal for functions, seems like you still lose the hoisting.
@JsonFreeman why ? if you insert _t
at the top of the file ?
import something from 'something';
myFunc(something.something());
@F("color") @G
function myFunc() {
doSomething()
}
import something from 'something';
var _t = function() {
doSomething();
}
_t = F("color")(_t = G(_t) || _t) || _t;
myFunc(something.something());
function myFunc() {
return _t.apply(this, arguments)
}
Also even if my proposal has a lot of issues, I would seriously like to be able to use decorators on function (even if I have to use variable assigned function) and lose hoisting.
Case like this gist seems a pretty good decorator use case for both function and class (especially coupled with ambient decorators if they still end up being in the scope)
@fdecampredon this does not work for the general case, as the decorators are expressions themselves. e.g.
myFunc(); // assumes function declaration is hoisted
var dec = (t) => t; // defininig a decorator
@dec
function myFunc() {}
if you hoist the function declaration and application of the decorator then you break the decorator. if you only hoist the function declaration, but not the decorator application you can witness the function in an undecorated state. no appealing solutions here.
this is the same issue as with class declaration extend clause, which in ES6 is an expression. the result was not hoisting the class declaration, just the symbol (akin to var declaration, but not function declarations)
Oups didn't think about it thank you @mhegazy.
However why the function part have completely abandoned the original @jonathandturner proposal had the rule :
decorated function declarations cannot be hoisted to the containing scope
Loosing hoisting is sure a drawback, but I find it damageable to transform it into an class only feature when it would have use case for other construct.
Let's see what the desired set of constraints seem to imply:
The only resolution I can see for this is the following: For function decorators, we only allow something of the form @identifier
. We do not allow a left hand side expression. In addition, the identifier must be a direct reference to a function declaration (including a decorated function). All function decorations that take place in the scope must be emitted at the top of the scope, in the order that they were applied.
The problem breaking hoisting rules is that it is surprising. If you are writing javascript for a while you expect function declarations to be available before they are lexically declared; now by adding a seemingly simple syntactic marker (the decorator) this fundamental nature of function declaration is altered.
Having said that, the ES7 proposal is still in its initial stages, so I expect it to evolve and expand; so it is conceivable that the final proposal would include functions in some form.
I do think they should be hoisted. I am saying that the only decorations we allow on functions are decorations that are definitely themselves hoisted. Namely identifiers that reference function declarations.
But there is another problem here. It is actually impossible to simultaneously hoist all decorated functions and ensure that they are not observed in their undecorated state. The problem can be seen with a decorator cycle.
@dec1
function dec2(target: Function) {
// Do stuff
}
@dec2
function dec1(target: Function) {
// Do stuff
}
Even if both functions are hoisted, which one gets decorated first? If dec2 gets decorated first, then dec1 will not itself be decorated by the time it is applied to dec2.
So we would have to choose between the following:
While I don't like either of these, I don't even think anything else is possible.
This is the JS proposal. so the engine would not know if the expression refers to a function declaration or not, with static analysis we could tell though. consider this:
@dec1
function dec2(target: Function) {
// Do stuff
}
dec2 = undefined;
@dec2
function dec1(target: Function) {
// Do stuff
}
Ugh! Javascript not getting any simplier anymore. :-)
It would be easier to enable it only on function _expressions_:
const foo = @decorator () => {
// ...
}
const bar = @decorator function() {
// ...
}
Function expressions or lambda's aren't hoisted.
Is it possible to have parameters like this in a decorator in typescript 1.5.0-alpha ?
@dec1({key1: value1, key2, value2})
function dec2(target: Function) {
// Do stuff
}
Ok nevermind, just create a factory which takes the parameters and return the actual decorator function.
For example, a class decorator with a string parameter:
function decoratorWithString(param: string) { // Decorator factory
return function(target) { // Actual decorator
// Do stuff with target and string parameter
}
}
// Usage
@decoratorWithString('foobar')
class Foo {
}
greetings.
i am trying to figure out how to write a decorator that will pick up the types declared in the constructor.
here's some code that illustrates what i'm trying to do, but it is hardwired. i need it to respond to the constructor declaration in class D
class A {
public message = "identity: class A";
}
class B {
public message = "identity: class B";
}
@decoTest
class D {
static metadata:Array<Function> = [];
constructor(a: A, b: B) {
}
}
describe("decorators", function() {
it("should inject constructor types", function() {
var d = new D(new A(), new B());
expect(D.metadata.length).toBe(2);
});
});
function decoTest<T>(target: T, ...rest) {
target["metadata"].push(A, B); // how do i get this based on constructor ???
return target;
}
I am afraid that type information is out of scope of the decorator feature.
You can use ParameterDecorator on each parameter to add that kind of information.
The typing or implementation for ParameterDecorator is not quite correct, from the typings the target is a function, but while using typescript, it is the prototype object.
I must cast the target parameter in order to get the right type:
function MyParameterDecorator (_target: Function, methodName: string, index: number) {
const target = <InterfaceForMyUseCase><anyt>_target;
// do stuff
}
Instead of:
function MyParameterDecorator (target: InterfaceForMyUseCase, methodName: string, index: number) {
// do stuff
}
wow - parameter decorators rule !!!!!!!!!!!!!!! see this repo
here's the output from running the tests:
LOG: 'injectMe:'
LOG: ' type: class A'
LOG: ' type: class B'
LOG: ' some key'
and the code:
module ParameterDecorators {
class A {
static typeName:string = "type: class A";
public instanceTypeName = "instance: class A";
}
class B {
static typeName:string = "type: class B";
public instanceTypeName = "instance: class B";
}
@injectTest(A, B, "some key")
class C {
static injectMe: Array<any> = [];
constructor(a: A, b: B) {
}
}
function injectTest(...rest) {
return function(target): void {
target["injectMe"] = rest;
}
}
describe("decorators", function() {
it("should inject dependency-injection keys", function() {
var c = new C(new A(), new B());
console.log("injectMe:");
for (let parm of C.injectMe) {
if (typeof(parm) === "function") {
console.log("\t" + parm["typeName"]);
} else {
console.log("\t" + parm)
}
}
});
});
}
I've made a wrapper around express (but any web framework could be supported, an adapter interface is defined) with a decorator based API: https://github.com/cybrown/web-decorators
I am using ClassDecorator, ParameterDecorator and MethodDecorator.
@cybrown I just updated the signature for ParameterDecorator
in #2635, which is now in master.
@rbuckton Thank you, I'll update that tomorow in my project.
Is it possible to have the name of the parameter in a ParameterDecorator ?
This may be useful because I think that the name of the parameter might be half of the time the first argument of a ParameterDecorator.
Moreover, it might be a solution to parameter name mangling by minifiers.
i've been asked to accomplish this:
@inject(A, B, "some key")
class C {
static injectMe: Array<any> = [];
constructor(a: A, b: B) {
}
}
function inject(...rest) {
return function(target): void {
target["inject"] = rest;
}
}
but without specifying the keys in the inject decorator, but instead having the decorators pick up the constructor's class functions automatically with something along these lines:
inject(@parameterTypes types:Function[]){ ... }
i'm looking for how to do what @jonathandturner describes here
@cmichaelgraham Yes having runtime type information (rtti as described in what was AtScript) would be awesome for that kind of usage, even in a completely separated feature such as introspection.
@cmichaelgraham look at https://github.com/Microsoft/TypeScript/pull/2589
@cmichaelgraham We are working on a proposal for ES7 to add a metadata API that will work together with decorators. #2589 adds experimental support for that proposal, but requires a separate polyfill be present to read the metadata.
@rbuckton @cmichaelgraham There was originally a design for special a TypeScript decorator that would tell the compiler to inject types into the decorator, based on the target being decorated. I think it was @parameterTypes
Am I correct in thinking that that feature is being removed in favor of the more general metadata api? If so, I would support that.
Can you talk a bit about the status of that with respect to TypeScript. Is that planned for the release of 1.5? If so, what will the compiler options look like? One thing that would be useful is to have the compiler automatically generate the type metadata for constructor signatures only.
@EisenbergEffect Decorators are part of the 1.5, you can start using it with our latest release 1.5-alpha.
As for meta data support. The desing has changed since the orignal proposal for @paramtypes
. the new desing is using Reflect.metada proposal. See #2589 for more details on how it works. also @rbuckton has a pollyfill to consume the metadata here: https://github.com/rbuckton/reflectDecorators
@mhegazy I know that. See here: http://blog.durandal.io/2015/04/09/aurelia-update-with-decorators-ie9-and-more/ :smile:
I'm also familiar with the metadata proposal. I've been providing feedback on that. I just wanted to know if the original @parameterTypes
idea was being removed.
thanks @EisenbergEffect for sharing. :+1:
Yes. the only issue with @paramtypes
is it makes emit directed by the type system, and requiring global program information. This breaks scenarios for single-module transpilation (see #2499). The other option was to put it on call sites, which adds a lot of work on the decorator users instead of authors. So we went back to the drawing board and landed on the Reflect.metadata approach instead.
if you also looked at earlier versions of the proposal, we had to remove the ambient/design-time decorators for the same reason.
Thanks for clarifying. That all makes sense. Any idea if the Reflect-based approach will land in 1.5?
yes. it is currently in master. it should be available in the next release. it is currently an opt-in feature using the experimental flag --emitDecoratorMetadata
; and it only adds metadata to decorated entities.
"it only adds metadata to decorated entities" Can you expand on that idea? Is a special decorator involved? Or does it add it to anything with any decorator? In other words, if a developer uses Aurelia's inject
decorator, then will that trigger the compiler to generate the metadata?
if a developer uses Aurelia's inject decorator, then will that trigger the compiler to generate the metadata?
yes.
what i meant is:
@inject
class Foo {
constructor(a: number, b: string) {}
}
class Bar {
constructor(a: number, b: string) {}
}
with --emitDecoratorMetadata
the compiler will emit type metadata (i.e. call to reflect.metadata('desing:paramtypes', [Number, String])
) for Foo
but _not_ Bar
.
That will work for us. We'll prepare support for the Reflect.metadata api for our next release, most likely next week. Thanks for the clarification!
so what is emitted for this?
``` language=javascript
@inject
class Foo {
constructor(a: A, b: B) {}
}
class Bar {
constructor(a: number, b: B) {}
}
would it be (for Foo):
``` javascript
reflect.metadata('desing:paramtypes', [A, B])
@cmichaelgraham Yes, that is roughly what is generated.
crazy cool !!!!
using gulp-typescript to build this repo - (nice work @ivogabe)
gulpfile build-ts
command (note the emitDecoratorMetadata
option):
gulp.task('build-ts', function () {
var tsResult = gulp.src([
'./views/*.ts',
'./typings/**/*.d.ts',
'./*.ts'
],
{base: "."})
.pipe(ts({
typescript: require('typescript'),
declarationFiles: false,
noExternalResolve: true,
target: "es5",
module: "amd",
emitDecoratorMetadata: true
}));
return merge([
tsResult.dts.pipe(gulp.dest('.')),
tsResult.js.pipe(gulp.dest('.'))
]);
});
turns app.ts
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';
@inject(Router)
export class App {
public router;
constructor(router:Router) {
this.router = router;
this.router.configure(config => {
config.title = 'Aurelia';
config.map([
{ route: ['','welcome'], moduleId: './welcome', nav: true, title:'Welcome' },
{ route: 'flickr', moduleId: './flickr', nav: true },
{ route: 'child-router', moduleId: './child-router', nav: true, title:'Child Router' }
]);
});
}
}
into app.js
var __decorate = this.__decorate || (typeof Reflect === "object" && Reflect.decorate) || function (decorators, target, key, desc) {
switch (arguments.length) {
case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);
case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0);
case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc);
}
};
var __metadata = this.__metadata || (typeof Reflect === "object" && Reflect.metadata) || function () { };
define(["require", "exports", 'aurelia-framework', 'aurelia-router', 'bootstrap', 'bootstrap/css/bootstrap.css!'], function (require, exports, aurelia_framework_1, aurelia_router_1, , ) {
var App = (function () {
function App(router) {
this.router = router;
this.router.configure(function (config) {
config.title = 'Aurelia';
config.map([
{ route: ['', 'welcome'], moduleId: './welcome', nav: true, title: 'Welcome' },
{ route: 'flickr', moduleId: './flickr', nav: true },
{ route: 'child-router', moduleId: './child-router', nav: true, title: 'Child Router' }
]);
});
}
App = __decorate([
aurelia_framework_1.inject(aurelia_router_1.Router),
__metadata('design:paramtypes', [aurelia_router_1.Router])
], App);
return App;
})();
exports.App = App;
});
of special interest is the emiited type metadata about the constructor parameters:
App = __decorate([
aurelia_framework_1.inject(aurelia_router_1.Router),
__metadata('design:paramtypes', [aurelia_router_1.Router])
], App);
so in theory, you could alter the inject decorator function to inject the proper classes without specifying them in the inject, but instead specifying the types in the constructor :)
@cmichaelgraham We can easily enable this today without altering the framework. The DI library from Aurelia has a hook container.addParameterInfoLocator
. You pass it a function that can take a type and return its dependencies. For our next release (next week), we can add this to the core configuration though so it's easy for TypeScript developers to turn it on. I would have put it in this week's release, but I hadn't realized that this was changed yet.
@EisenbergEffect brilliant !! :+1:
I used annotations to mark properties of an object as observable, which transforms a primitive into an observable object (for those interested, https://github.com/mweststrate/MOBservable/commit/8cc7fc0e20c000db660037c8b5c9d944fe4155d9).
However, especially for properties, it felt a bit unnatural that the annotations are applied to the prototype, and not in the constructor of a class itself, this means that it is hard to either get the the initial value, or even the this
. The work around for that was to create a property, and in its getter/setter perform the actual logic that the annotation was supposed to do, since the this
and the value are available then.
Besides that, awesome feature!
Using Metadata Reflection API I was able to do a simple properties dependency injection.
You can check required changes here https://github.com/matjaz/property-DI/commit/2b4835e100b72d954b57d0e656ea524539ac17eb.
Shouldn't be __metadata decorator in generated code invoked first of all decorators? I wanted to create simple deocrator using class's metadata, but __metadata('design:paramtypes', [TYPES....]) is invoked as last, so i can not get these data from Reflect
@ufon can you share the code you are looking at?
Sure, here https://gist.github.com/ufon/5a2fa2481ac412117532
EDIT:
my bad, there is some other mistake in my code, cannot get types even after class inicialization
@ufon, i am not sure i see the issue then. __metadata is the last item in the decorator list, this means it is the first to execute, and definitely before inject gets to execute.
House = __decorate([
inject_1.inject,
__metadata('design:paramtypes', [Floor, String])
], House);
where __decorate
definition uses reduce right to execute the decorators in reverse order.
decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);
This is to match the spec, that decorators (as expressions) are evaluated in order of declaration, but executed in the reverse order, allowing outer decorators to consume results from inner decorators.
I see, sorry for annoy :) my bad, there is some other mistake in my code, cannot get types even after class inicialization
how are you querying for them? and do you have a pollyfill for Reflect.metadata?
added to the gist...
Reflect.getMetadataKeys(House);
this results empty array..
Yes, I'm requiring 'reflect-metadata' package
looks like the polyfill loosing it, @rbuckton can you take a look.
Think I got it...
requires are moved after typescript's helper code
var __metadata = this.__metadata || (typeof Reflect === "object" && Reflect.metadata) || function () { };
require('reflect-metadata');
so, in time var __metadata is beeing inicialized, polyfill is not loaded yet
but i cannot get these requires before the generated code
EDIT: updated the gist.. when single module is compiled, there is no way to load polyfill before __metadata is resolved, because typescript's generated code is always moved to the top of the file
ooh.. this is a bigger issue. logged issue #2811 to track it
Until then you require reflect-metadata
in another module (you need two files).
see https://github.com/matjaz/property-DI/
There appears to be an invocation inconsistency which makes developing decorators with variable/optional parameters pretty difficult to do. As an example:
@F
prop: number;
@F()
prop: number;
In the former, F gets invoked with the parameters (target, propertyName, propertyDescriptor) while in the latter, F gets invoked with the the parameters () and requires you have an internal function returned which in turn gets invoked with (target, propertyName and propertyDescriptor).
My _assumption_ would have been that both @F and @F() would be equivalent and therefore you can only perform either call if the decorator supports 0 or more parameters.
This is particularly important in the case:
@F // Performs some default behaviour.
prop: number;
@F({ option: true }) // Performs some configured behaviour.
prop: number;
This can be worked around by just explicitly calling @F() instead of @F but it's easy to make that mistake and then be confused when your decorator doesn't work even though by the looks of it it should.
In this case I would like to do the following:
export function F(options?: any): PropertyDecorator {
return (target, name) => {
// do something.
}
}
and be done with it, but instead I have to do something like the following crude example:
export function F(...args: any[]): any {
var func = (target, name) => {
// do something.
}
if (args.length === 1) return func;
else if (args.length === 2) func(args[0], args[1]);
}
Which is a real pain.
@Tharaxis this behavior is by design. @F
and @F()
are not equivalent. @F
calls a decorator F
, where as @F()
calls a decorator factor F
with empty argument list, and then applies the resulting decorator.
The problem here is that the compiler compares the signature of the decorator factory F
against declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
which ends up to be assignable. i think we need a stronger check here to make sure we catch these issue. I have logged #3246 to fix this. I believe you are doing the correct thing, and the compiler should catch invalid uses of a decorator factory.
@mhegazy I'm not sure why this is the case however, is there some use case that precludes one making @F
and @F()
equivalent, since ultimately they are both an identical invocation of the decorator function, just one of them happens to also include an invocation to an outer factory function? It seems like the principle of least astonishment is being violated here since this is definitely quite astonishing behaviour and I don't see why there have to be 2 ways to invoking a decorator from the perspective of the API consumer.
While I get that from a JS perspective there is a difference (in one you are passing the reference of the F function and the other you are executing a function and then passing in the reference of its result as the decorator), I'm not sure I understand why one cannot just have decorators that are passed as @F
implicitly match (and invoke) the factory as one with an empty parameter list?
The compiler has no way to know if it is a factory or a decorator. if you have this signature:
declare function decoratorOrFactory (...args: any[]): any;
Is this a decorator or is it a factory, should it be called with empty arguments or not?
The most the compiler can do is verify that the signature matches when calling it one way or the other, and give errors if it does not.
@rbuckton has a fix for #3246 that should flag the cases where your decorator is not used as a factory, so stay tuned.
@mhegazy, what I'm saying is that the use of a decorator (i.e. @F
or @F()
) should _always_ be an invocation of factory function F, which in turn returns the appropriate decorator type. There is no top-level representation of a decorator without an encapsulating factory.
In other words the signature of F is always:
declare function F(...args: any[]): ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator;
Or some equivalent.
Then invocation @F
is made the equivalent of @F()
by the compiler. Since both the signature of @F
and @F()
must match the factory signature in some form this solves the problem of invocation confusion. There is only one way to invoke a decorator!
Given the following:
declare function F(...args: any[]): PropertyDecorator {
return (target, name) => {
// do stuff.
}
}
@F()
property: number;
and
declare function F(target, name) { // Matches PropertyDecorator
// do stuff.
}
@F
property: number;
They do the same thing but I have to declare them differently! However, declaring F so that it can exist both ways is really cumbersome and makes creating consistent parameters and documentation impossible. You're either choosing that your decorator needs to use a factory or not, and your API consumer has to know this distinction before hand to make use of it.
@Tharaxis If I'm looking at a library providing F
that will be used as a decorator, I absolutely expect @F
and @F()
to be different things. Just like I would expect C
and C()
to be different things - the first references a value, the second invokes a value. @F
applies a decorator, @F()
invokes a function that will create a decorator. Those are not, and should not be, the same operation.
@DavidSouther is there any valid reason why this cannot or should not be the case? There is no functional difference between an invocation of @F
and @F()
but there is a pretty big difference between how they are defined and documented, and it adds additional complexity to invocation that should be unnecessary.
My proposal is that there should be not be two ways to define a decorator because there is no need, and there should be no need to define a decorator more than one way.
There is no functional difference between an invocation of @F and @F()
I think this is the main point of contention. i do not think this is true. F is a reference to a function, F() is a reference to the return value of F when called with an empty argument set (i.e. F.call(this, [])
); and these are two functionally, and conceptually different things.
@Tharaxis @F
and @F()
are different things. There is a difference in how they are used, a difference in how they are documented, and differences in their invocation that is absolutely necessary.
Let me ask it a different way: why must every decorator function be a factory? Using your proposal, it would be impossible to have a simple decorator.
@mhegazy while I will concede that the latter option (@F()
) does result in the generation of a functional closure and additional overhead through the creation of isolated (and duplicated) decorator functions and the @F
invocation essentially performs against a shared decorator function reference, I would say that the isolation through closure would be "safer" and more consistent, reducing the likelihood of surprises.
@DavidSouther Your question depends on what you define as a simple decorator. Nothing precludes one having a simple decorator in the form of:
declare function F(): PropertyDecorator {
return (target, name) => {
// do stuff
}
}
I find this pretty minimally invasive since it's only 2 lines more than the "simple syntax" and defines more obviously what the available decorator invocations are. Additionally, since there is then only a single way to define a decorator (and let us not discount the value of consistency), you'll probably take roughly another 2 seconds to whip this out compared to the "simple syntax".
Additionally a more problematic issue is as follows:
declare function F(options?: any): PropertyDecorator {
return (target, name) => {
// do stuff
}
}
@F()
property1: number;
@F
property2: number;
@F({ option: true })
property3: number;
One of these things is not like the other. The use of @F
will not function even though at face value it definitely should. Keep in mind, pretend I am someone who is not aware of what the underlying declaration of F looks like, but instead just know that F exists and that it can take an optional set of arguments. The chances of me making a mistake with @F
is not trivial under the current mechanism. This puts a large onus on the developer/documenter to make sure that the consumer is aware that @F
will not work (but maybe @C
will because it's "different" somehow).
If I want a "one size fits all" decorator, where the consumer doesn't have to worry, I have to do this ghastly thing:
declare function F(...args: any[]): any {
var decorator = (target, name) => {
// do stuff
}
// Heaven forbid your decorator formal parameter list also can take 2 parameters.
return (args.length === 2) ? decorator(args[0], args[1]) : decorator;
}
Which is quite frankly horrendous. Additionally, I lose all valuable parameter intellisense against decorator F since now it has to be generalised.
There is a lot to be said to having decorators be easy and consistent to define and document. Lets not fool ourselves into thinking that everyone consuming our code will have the same savvy or understanding as us who are the creators of that code.
@Tharaxis I thought about this early on in the design for decorators, and I understand your specific points. As @mhegazy and @DavidSouther mention previously in this thread, this behavior is consistent with the standard behavior of functions in JavaScript.
You are correct that any attempt to create a function that can act as both a decorator and a decorator factory can be somewhat complex. In general though, it may be better to employ a best practice to always use decorator factories, and supply rules to a linter to ensure this practice is adopted.
Also, I have just submitted PR #3249 to address your earlier point regarding inconsistencies in the type checking for decorators.
Hi @rbuckton,
I'm one of those not so savvy users of decorators mentioned by @Tharaxis...
I guess the question I would have is should decorators be consistent with functions? I.e. In the case of functions, returning this.f versus this.f() makes complete sense to me as sometime I want the value, sometimes I want the thing that produces the value.
In the case of decorators it doesn't seem quite as clear cut to me at the language feature level. I would like to argue that I just want to apply decorator @F, I don't really want to know whether it's been implemented as a factory or static method. That is unless I would loose some capability that decorators would otherwise have.
My apologies if the above is misinformed or ignorant, I'm relatively new the javascript world.
Thanks & cheers
I also have to ask that question - does it make sense to maintain syntactic parity with with functions _if_ (and only if) there is no real reason to do so other than "because it's defined using functions". I see Decorators as an entirely unrelated feature to functions which just _happen_ to be defined using them.
Right now the fact that @F
and @F()
are not identical causes the following issues:
@F
and @F()
and why sometimes this may work and why sometimes this may not work (and the need to use @F
vs. @F()
could be entirely framework/code/usage specific - therefore inconsistent from a consumption standpoint: Imagine declare function A(params?: any): ParameterDecorator
vs declare function B(target, name)
, @A
won't work, but @A()
will, @B
will work but @B()
won't, yet cognitively they're the same thing, an application of a decorator with no arguments).@F
) and now you want to add some parameters (but make them optional for backwards compatibility). Under the current method, all those @F
applies will now need to be refactored to @F()
- some within code you may not have access to - while implicit invocation would allow you keep things working and restrict the change to your code.@Tharaxis I understand that the distinction between @F
and @F()
can be a high cognitive burden on consumers. However, it sounds like you are asking for the emitted code, and therefore the behavior, to change, so that the two behave equivalently. This would likely conflict with the ES7 proposal for decorators, since the two forms are very unlikely to be treated the same. It will not be good if this leads to a divergence.
Another point is that the confusion here is akin to the confusion between passing a function as a callback, and calling the function. If you had this
function callbackFactory() {
return function(...args: any[]) { // do stuff };
}
function takesCallback(cb: (...args: any[]) => void) { }
takesCallback(callbackFactory());
A user may get confused and call takesCallback(callbackFactory)
without invoking callbackFactory
. This is really just like the confusion you pointed out with decorators, but if the language normalized these two forms to do the same thing, there would be a loss in expressivity. Sometimes the distinction is important, and needs to be maintained. Not sure if this commonly applies for decorators as well, but in theory it certainly could.
@JsonFreeman I don't really get your argument though. You're saying that you can't make decorators make sense as their own entity because an ES7 proposal exists for decorators, but that's the thing, ES7 decorators are just that, a _proposal_, and as far as I'm aware, they're partly based on a combination of the decorator work described by both Google and yourselves, correct? Nothing has been decided on yet as to how they are to work, and it is likely that the proposal will go through _many_ iterations prior to approval, so why not have them working in a consistent manner in TS and then (at least try) get the various parties involved in the proposal behind the idea, using experience gained in TS as a basis? This would not be the first time TS has implemented a feature only to have to refactor later because the ES6 spec called for slightly different behaviour. You'll _always_ have that problem as long as you're tracking against a spec that does not yet exist and has not yet been agreed upon.
As for your example regarding functions, it appears yet again we're is conflating functions with decorators when they are in reality (and should be) completely different constructs. When I'm working with a function in JavaScript, I understand how functions are used and I understand the difference between a function reference and a function call (and when I might want to use one vs. the other) - and when I describe something as a function, there is no confusion as to what usages it has. Decorators are _not_ functions and should not inherit behaviour of functions. Functions are merely the method upon which decorators are constructed. That's like saying classes are functions, which is not the case, functions are merely the way though which we describe classes prior to ES6 because there was no more obvious method available. Classes don't take on the properties of functions, you cannot call classes like functions, but you do declare them using functions prior to ES6.
Regardless, I feel that I've exhausted my points of argument on the issue. I'll just have to work around the choice. In my own code I'll always make use of factories for the sake of my own consistency. I still believe treating decorators literally as functions is likely to cause plenty frustration in the future, but that's just me.
@mhegazy This is the same issue I brought up on the @jonathandturner decorator repo here: https://github.com/jonathandturner/decorators/issues/16 I do think this is something that should be thought about. As it stands, I expect this design choice to be the subject of many blog posts titled something similar too "ES7 Decorator Pitfalls".
@Tharaxis, I agree with your first point. I don't think an ES7 proposal should prevent us from designing something well, and our design is indeed one of the precursors to ES7 anyway.
As for the "decorators are not functions argument", if I'm not mistaken, we are not discussing decorators themselves, we are discussing decorator factories. And regardless of any distinction between decorators and functions, I think decorator _factories_ are actually just functions. And it sounds like you are asking for the compiler to automatically generate a call to a decorator factory, which is an ordinary function. If that happened, there would be no way for the programmer to distinguish between the decorator factory and the decorator.
Also, the nice thing about treating decorators as functions is that a consumer can apply it as a decorator or by simply calling it as they see fit.
@JsonFreeman the argument revolves around the current situation where there are two ways to both describe and invoke a decorator, one through a factory function, and then one as a "raw" function (which in the factory case is the result of a factory call), and the question was more about "shouldn't there just be one way, and have factories be that way".
What I am asking is yes, could we not just have the compiler turn @F
into the equivalent of @F()
call and have type checking require an argument list of 0...n parameters when using the unbracketed syntax. Perhaps you could elaborate on what is meant by "...there would be no way for the programmer to distinguish between the decorator factory and the decorator...", since I would think it's very easy to distinguish. The decorator is always the response from the factory, and the name of the factory is the decorator's name... not that difficult, or am I misunderstanding?
As for your last point about allowing the consumer to just apply the decorator, if it's well described that all decorators use factories, it's pretty easy to then invoke a decorator yourself, you just do <decorator name>(<argument list>)(target, name)
compared to <decorator name>(target, name)
as an example. Keep in mind that mandating the use of factories would mean the former example will always work, while not mandating it will result in the latter example sometimes not working, and is entirely dependent on how the decorator is implemented - a headache just waiting to happen.
I feel it necessary to point out, I have no problem with decorators using functions, my issue is however that having two ways to describe the same thing leads to consistency issues. Especially when it those two ways also mean your method of invocation has to differ. This is a disparity that hinders everything from refactoring to language consistency.
The refactoring problem I described a few posts back should already be more than enough of a reason why the current method needs to be inspected.
Hi all, just a final 1.5 cents worth from a regular user along the lines of what @Tharaxis has said.
I'd be more than happy with current proposal if:
a) the universe mandates it.
b) decorators will loose important capabilities if things were different.
The later is of course a value judgment, to which the answer will depend to some degree on the type of developer you are (i.e. general user / expert user etc). I'm in the former category and am generally spread thinly across multiple languages and frameworks. So for me 'important capabilities' aren't going to include flexibility to write decorators 2 different ways. Examples of all the tradeoffs would be great if they exist.
Ideally it'd be great if @F and @F() could be consistent regardless of how decorators are implemented, but if not I'd vastly prefer to be constrained to using factories when writing decorators rather than having to avoid rakes in the grass whenever I'm using them.
Thanks & cheers
It seems like this request hinges on the idea that decorator factories are the canonical way to use decorators, but I don't see how this allows the user to define and use raw decorators. If I define a decorator F
, and the application @F
gets treated as @F()
, then the result of calling F is used as the decorator, instead of F itself. Are you suggesting we give an error somewhere if someone tries to apply a raw decorator instead of a decorator factory?
This idea feels like it would reverse the natural compositionality of decorators and decorator factories. Decorators definitely feel like the primitive construct here, with decorator factories being a layer of abstraction built on top of decorators. They are just a pattern, nothing more. If instead the decorator factory became the canonical thing, the primitive, then people would define a bunch of decorator factories, that take no arguments and return a flat decorator. This would start to feel very silly, and it would reverse the natural intuition of what is considered basic and what is considered more complex.
One thing I am very wary of with decorators is an excess of magic. I personally get nervous when I don't understand what the code is doing, and if the compiler is secretly adding extra invocations that the programmer did not write, that feels to me like too much voodoo.
Hi @JsonFreeman,
As I mentioned my preference would always be for the ugliness to be with the author of the decorator rather than the user. However, I agree that lots of no arg factories is a whole lot of ugly. Could a decorator be used to fix this? E.g.
// wraps rawDecoratorMethod in a no-arg factory method.
@Decorator
function rawDecoroatorMethod(target, name, descriptor) {...}
// looks like this one would be a no-op... so that feels not quite right unless there's other advantages.
@DecoratorFactory
function decoroatorFactoryMethod(someArg) {...}
Anyway, if such an approach could be found to make sense it has the advantage that the annotation documents the purpose of the function. i.e. it decorates.
Cheers
Good gravy, decorators to help define decorators, a serious case of Typeception.
@JsonFreeman I'm not sure having zero-param functions are necessarily any more ugly than having a multitude of (target, name) functions in their stead. If anything at least the zero-param functions provide a level of specificity/explicitness that the other method lacks (since the parameters don't ever match the invocation in the latter case), and on top of that provides a single target to document instead of an unnecessary level of inconsistency. I also see a reluctance to go any one route due to a perceived "possible" complexity, but I would say in the light of the very real "obvious" complexity the current implementation imposes on the consumption side, one should err more on the side of the obvious than the possible.
The other option is to mandate that decorators which are called as @F
are able to match either the pattern for ClassDecorator, MethodDecorator, PropertyDecorator or ParameterDecorator, OR a 0..n arg factory function that returns ClassDecorator, MethodDecorator, PropertyDecorator or ParameterDecorator. However I would feel that that implementation would cause other errors (what if you have two conflicting functions, which one would be considered best possible match?) and would just add undue complexity within the compiler. Simply converting @F
calls to @F()
would be the simpler solution and would solve the various problems stated.
Sorry, to clarify, I wasn't claiming that your solution is complex. I meant that automatically invoking the decorator before applying it is opaque. The inserted invocation is invisible to the user, and I feel like many users will not expect it.
I think the way it currently stands is not complex either. As you point out, it allows library authors to be vague or wishy-washy about whether their function is meant to be applied as a decorator. That I will grant you. But a good library will make it clear.
In the scheme you suggest, where you do the invocation automatically, what happens when you use an arbitrary decorator expression instead of just an identifier? Does that get invoked too?
I'll agree, that unless documented otherwise the implicit invocation is likely to be surprising, but only to those that haven't used concepts such as C# attributes and the like.
Could you elaborate on what you mean by an arbitrary decorator expression?
Good gravy, decorators to help define decorators, a serious case of Typeception.
Fair call. Serves me right for a poorly thought-out comment late on a weekend afternoon. Lesson learnt. Internet.undo().
My point being that if it turns out that consistent call site syntax mandates the use of factories then I'm more than happy with that. I'll no doubt write a decorator to remove the boiler plate. Again I'd prefer a little pain when writing the decorator over repeated pain using them (albeit potential pain at this point). Others will disagree.
Isn't there also the issue with API enhancement? A decorator created without a factory can't have optional parameters added at a later time, hence I predict @F
and @F2()
in my future.
I also don't see that you'd get this scenario when using f
and f()
, they're different use cases on the consumption side. In the decorator case I'm always applying/invoking the decorator on the target right then and there, and there's always a method invocation going on behind the scenes.
But the crux of this for me is the usability issue, when I'm applying a decorator I don't want to have to google to find out how the author implemented it - I'm interested in the behaviour, not the packaging.
Cheers
Just a final note to summarise and then I'll keep quiet. In short, this is really just an interaction design issue for me. I'm really keen to see decorators designed from the outside in and not the other way around.
As a UI/UX guy I see this fairly often. I've worked with talented developers who have suggested UI solutions that would hurt users (one example was two save buttons in one case to solve a transaction complexity - again a smart guy and good human). I just think when you're knee deep in complex implementation details logic it can be hard to forget what you know and see it through the eyes of average user.
Now if as the average I'm just plain wrong, or if the complexity mandates the current design then all good, I'll just have to get my brain on and learn.
Cheers
If your decorator is not an identifier, but some other expression, would you automatically invoke it?For example, let's say it was a decorator. OR-ed with a fallback function expression. Something like:
@(F || function (target) { // default decorator behavior })
class C { }
Do you then automatically invoke this? That would give you the wrong result because you end up invoking the default decorator before it's applied:
(F || function (target) { // default decorator behavior })()
This does not seem right.
@JsonFreeman, does the decorator syntax allow for arbitrary expressions like that? I'm not 100% certain allowing developers that much rope with which to hang themselves with is necessarily a good idea, but that's just me (purely because I don't see arbitrary expressions like that solving the reuse/boilerplate issue decorators aim to solve and instead just serve to make the code look ugly and even more difficult to follow).
That said, I suppose the only way it would work then would be if the arbitrary expression itself evaluates to the same factory syntax, so:
@(F || () => function(target) { /* default decorator behaviour */ })
class C { }
I agree it does result in you having to put a few more fiddly bits around your arbitrary decorator, but I think one has greater issues than the addition of () =>
if you're using arbitrary expressions as decorators.
NOTE: I obviously did not mean that lambda syntax would be the only way to declare it, but () =>
is slightly nicer than function() { return function(target) { /*...*/ } }
.
Really sorry to interrupt this decorator calling syntax debate, but could anyone officially clarify the order the decorator expressions are called? Especially, along the lines of decorator target type, the position of the target member in the original source and the order of the decorators on a single target.
@billccn The decorators are applied from bottom to top. So in the original source code if you had
class C {
@F
@G
method() { }
}
This would first apply G to method, and then apply F to the result. Is this what you are asking?
How about for this:
@A
class Clazz {
@B
prop = 1;
@C
method() {}
@D
get prop() {return 1;}
@E
method2() {}
@F
prop2 = 1;
}
they follow scope order, so all properties/methods first, in order of declaration, then the class one. i.e. B, C, D, E, F, A.
you can see the generated code here.
It seems it is not currently possible to decorate parameter properties. Can they be supported?
Parameter properties are thing like constructor(private prop: Type)
, where a property (field) and a parameter is declared together. The problem is both of them can be decoreated, so some imaginative syntax may have to be invented.
Question: Can the decorators be used to modify the behavior of a plain object method, like...
var isCreatorUser = () => { /* code that verifies if user is the creator */ },
isAdminUser = () => { /* code that verifies if user is admin */ },
var routeConfig = {
get() {},
@isCreatorUser patch() {},
@isAdminUser @isCreatorUser delete() {}
};
... and if not, why? It will be really useful.
Not yet, but we are considering it. @rbuckton might have more details.
May I ask why decorator cannot change the decorated result type from the type being decorated?
For example, MethodDecorator can be alternatively proposed as below:
declare type MethodDecorator = <T, R>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<R> | void;
So decorator can be more flexible to use and works somewhat like a macro. The type of decorated class / field can be updated correspondingly to decorator returning type, so that type safety is still kept.
I know that https://github.com/jonathandturner/brainstorming/blob/master/README.md#c4-defining-a-decorator has stated the returning type should be the same. But type validity
isn't a valid argument here, IMHO. First, decorators in JavaScript can alter the class/field type, so there is no compatibility issue between TS and JS. Secondly, decorators are applied statically, TS compiler can reason about the original field type and decorated field type at define site.
@HerringtonDarkholme I would say this needs to be tracked in a different suggestion. The main reason is the way the compiler reasons about types is through declarations. once a type is declared it can not change. adding type mutators in the mix means we need to resolve these as we are resolving the declaration which can get hairy.
@mhegazy so, can I understand this as a compromise for implementation ease, rather than a deliberate design?
According to this proposal, decorators restore the ability to run code at design time, while maintaining a declarative syntax. So the following code shall be
valid in future's JavaScript, but not valid in today's TypeScript (however this might be accepted by TS one day, right?).
function SafeCtor(target) {
var safe = function(...args) {
var instance = Object.create(target.prototype)
target.call(instance, ...args)
return instance
}
safe.prototype = target.prototype
return safe
}
@SafeCtor
class Snake {
constructor(name) {
this.name = name
}
}
var snake = Snake('python')
alert(snake instanceof Snake)
I know the chances for supporting this feature is small. But I want to confirm this is not a similar story like structural
vs nominal
typing.
The current check is that you have to return something from your decorator that is assignable to the target. the missing part is we are not capturing any additions to the type. I do not think we have an idea on how to do that, but we have not really thought about it either. so i think it is a fair suggestion, but the cost to implement it may be very high.
Ideally, I think the type returned by the decorator should be identical to the target. There's no telling whether you will use the decorated type in a source or target position in an assignment.
Although I guess really we should merge the decorator return type and the target, kind of like a mixin.
Just some additional functionality idea. Since TypeScript compiler output is JavaScript, there are some similar languages for Java, like Xtend
You can learn about some interesting ideas on this project. Look for Active Annotations "Active annotations allow developers to participate in the translation process of Xtend source code to Java code via library"
Controlling the TypeScript output via decorators will be a really nice feature!
Decorators for interfaces, should be possible.
@shumy The problem is that interfaces do not exist at runtime in any way, and decorators run purely at runtime.
Is there a reason to treat property decorators differently than method decorators with respect to return value? (Babel doesn't).
What I mean is that, if I want to define functionality for a property via decorator I have to do this:
function decorator(proto, name) {
Object.defineProperty(proto, name, { value: 42 });
}
class Foo {
@decorator
a: number;
}
Whereas Babel requires the descriptor to be returned:
function decorator(proto, name) {
return { value: 42 };
}
class Foo {
@decorator
a;
}
This makes it difficult to write a decorator in TypeScript which is part of a library which will be used from Babel.
I would propose that a property decorator be treated identically to method decorator, and the return value of the decorator should be applied via Object.defineProperty
. This would also allow for multiple decorators on a single property, just like a method.
A workaround for now (for Babel compatibility) is to return the descriptor from the decorator in addition to setting the property, but that seems unnecessarily wasteful:
function decorator(proto, name) {
var d = { value: 42 };
Object.defineProperty(proto, name, d);
return d;
}
@mhegazy would you happen to have any insight on my comment above?
@sccolbert In TypeScript we opted disallow the use of property descriptors for decorators on data properties, as it can lead to issues at runtime due to the fact that any "value" specified by the descriptor will be set on the prototype and not on the instance. While your example above would generally not be an issue, consider the following:
function decorator(proto, name) {
return { value: new Point(0, 0); }
}
class Foo {
@decorator
p: Point;
setX(x) { this.p.x = 1; }
}
let a = new Foo();
let b = new Foo();
console.log(a.p.x); // 0
b.setX(10);
console.log(a.p.x); // 10 (!)
There is a proposal for a future version of ES to support an "initializer" property on the descriptor, which would be evaluated during the constructor. That would give you the ability to control whether the value is set on the prototype or allocated per-instance.
For now you can work around this limitation by calling Object.defineProperty
directly, as per the example in your comment. We will continue to investigate whether to relax this restriction in the future.
@rbuckton thanks! Are you guys in talks with Babel to converge on the same semantics? I think that's the most important thing.
Why would it be useful to use a decorator in order to specify a value for a particular instance? Isn't that what the property initializer is for?
My use case is more complicated that the example. I'm actually using the decorator to define a getter which returns an object bound to the this
context of the property, so that methods on said object have access to the instance on which it was defined.
You can think of this as emulating the descriptor protocol in Python, where accessing method bar
defined on class Foo
through instance foo
(i.e. foo.bar
) invokes the __get__
method of the function which returns a BoundMethod
. When that object is called, the underlying function is invoked with self
as the first argument, which in this case is foo
. Javascript doesn't have this concept, which is why we have to pass around thisArg
and call function.bind
all over the place.
For my case, I'm not defining methods, but a type-safe signal. Here is the decorator:
https://github.com/phosphorjs/phosphor-signaling/blob/1.0.1/src/index.ts#L144
When the property is accessed, it returns an implementation of ISignal
which is bound to the this
context of the owner. This allows the signal to refer back to the instance on which it was defined:
https://github.com/phosphorjs/phosphor-signaling/blob/1.0.1/src/index.ts#L263
So in effect, I'm using the decorator as a shortcut for this verbose equivalent:
class Foo {
valueChanged: ISignal<number>;
}
defineSignal(Foo.prototype, 'valueChanged');
@rbuckton
Decorators are not allowed when targeting ES3
Why? I can't see anything that would prevent the use of __decorate
in ES3.
It uses the property descriptor, which is not available in ES3
Le 4 sept. 2015 2:03 PM, "Koloto" [email protected] a écrit :
@rbuckton https://github.com/rbuckton
Decorators are not allowed when targeting ES3
Why? I can't see anything that would prevent the use of __decorate in ES3.
—
Reply to this email directly or view it on GitHub
https://github.com/Microsoft/TypeScript/issues/2249#issuecomment-137717517
.
@sccolbert You might do better for that with ES6 proxies rather than property decorators.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
@DavidSouther browsers don't support Proxies yet :(
@cybrown Yes, it uses the property descriptor for methods and accessors. I tested on plain properties (fields w/o accessors) only. But it seems that decorators can be allowed in ES3 for properties (w/o accessors) and classes. It would be helpful.
And a fake property descriptor can be used for methods when targeting ES3. Something like { writable: true, enumerable: true, configurable: true }
. So I can't see any reason to not support ES3.
@sccolbert I see. That makes sense. Incidentally, TypeScript is working on improved _this_ typing for functions and methods. I wonder if that would be of any help here. Although I suppose typing is not the issue for you, it's runtime semantics.
@JsonFreeman Improved _this_ typing sounds intriguing for some of my other use cases. Do you have any more info on that?
I think the most developed discussion on _this_ typing is at #3694.
Cheers!
error TS1207: Decorators cannot be applied to multiple get/set accessors of the same name.
@get
public get myValue():any{...}
@set
public set myValue(value:any){...}
code above is not allowed even it make more sense compare to
@get
@set
public get myValue():any{...}
public set myValue(value:any){...}
Getter and setter are defined in one call to Obect.defineProperty. This rather a js quirk, the declaration of set and get though separate, really are the same property declaration. The error check in the compiler is to alert users when thinking of them separately; the decorators are applied only once to the property descriptor.
just wondering since the compiler can sense the get and set with same name and combine into single Object.defineProperty, why not also combine the decorator?
or perhaps an option flag to tell compiler to combine them, and leave a warning message instead of throwing error.
thank you
@TakoLittle: The reason we don't do this today partially stems from how decorators are composed. Decorators follow the same principals as Mathematical function composition, where (_f_ ∘ _g_)(_x_) is composed as _f_(_g_(_x_)). In the same sense, it can be thought that:
@F
@G
class X {}
Is approximately:
F(G(X))
The compositionality of decorators breaks down when you decorate both the getter and the setter:
class C {
@F
set X(value) {}
@G
get X() {}
}
How do F
and G
compose here? Is it based purely on document order (i.e. F(G(X))
)? Are each set of decorators for the getter and the setter discrete, and then executed in document order (i.e. G(F(X))
)? Do get
and set
imply any specific ordering (i.e. is the get
always before the set
or vice versa)? Until we're 100% certain the most consistent approach that doesn't surprise users, or have a well documented approach that is part of the decorators proposal with at least stage 2 or better acceptance within ECMA-262, we feel it is best to be more restrictive and error here as it allows us to relax that restriction at a later date without introducing a breaking change that could easily go unnoticed and possibly result in unexpected behaviors at runtime.
@rbuckton thank you so much for detailed explanation
TS team great work!! ^^d
Where is the documentation for this? and care to link the implementation commit?
Thanks.
@mhegazy What is the status on the implementation of the latest version of the spec. I understand there are some changes there.
This issue tracked the original version of the proposal. since this is completed we are closing this issue. for any updates to the spec, we will log new issues and outline all the breaking changes. I do not think the proposal is at a place now to be ready for us to jump on it. We are working closely with @wycats on the new proposal.
@omeid, you can find documentation at https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md
@mhegazy Thank you for the update. I'd love to stay informed. When you create the new issue for the spec update, please link it here so I can be notified and follow. The Aurelia community makes heavy use of decorators and we'll want to synchronize with both TypeScript and Babel on the update. Again, thanks for the great work the TS team is doing!
Function decoration is need of course.
Are there also plans for decorating of other objects in the code?
Most helpful comment
Function decoration is need of course.
Are there also plans for decorating of other objects in the code?