Skip to content

Decorators #2249

Description

@rbuckton

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 system

Decorator targets:

Class constructor

@F("color")
@G
class Foo {
}

desugars to:

var Foo = (function () {
    function Foo() {
    }
    Foo = __decorate([F("color"), G], Foo);
    return Foo;
})();

Methods

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;
})();

Static method

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;
})();

Properties

class Foo {
    @F("color")
    @G
    prop: number;
}

desugars to:

var Foo = (function () {
    function Foo() {
    }
    __decorate([F("color"), G], Foo.prototype, "prop");
    return Foo;
})();

Method/Accessor formal parameter

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;
};

Decorator signatures:

A valid decorator should be:

  1. Assignable to one of the Decorator types (ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator) as described below.
  2. Return a value (in the case of class decorators and method decorator) that is assignable to the decorated value.
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;

Notes:

  • Decorating a function declaration is not allowed as it will block hoisting the function to the top of the scope, which is a significant change in semantics.
  • Decorating function expressions and arrow functions are not supported. The same effect can be achived by applying the decorator function as var x = dec(function () { });
  • Decorating function formal parameters is currently not part of the ES7 proposal.
  • Decorators are not allowed when targeting ES3

Activity

  1. fdecampredon commented on Mar 7, 2015

    @fdecampredon

    Excuse me from what I understand of the spec, we won't be able to do:

    @F
    function test() {
    }
    

    Am I right ?

  2. ivogabe commented on Mar 7, 2015

    @ivogabe
    Contributor

    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; // ???  
        }  
    }
  3. MgSam commented on Mar 7, 2015

    @MgSam

    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?

  4. JsonFreeman commented on Mar 7, 2015

    @JsonFreeman
    Contributor

    What is the rule for refining the interpretation of CoverMemberExpressionSquareBracketsAndComputedPropertyName?

  5. JsonFreeman commented on Mar 7, 2015

    @JsonFreeman
    Contributor

    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?

  6. JsonFreeman commented on Mar 7, 2015

    @JsonFreeman
    Contributor

    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.

  7. JsonFreeman commented on Mar 7, 2015

    @JsonFreeman
    Contributor

    For the decorated exports, what is [lookahead ≠ @] for? Can HoistableDeclaration and ClassDeclaration actually start with a @?

  8. jayphelps commented on Mar 8, 2015

    @jayphelps

    This is a dup of #1557

  9. JsonFreeman commented on Mar 8, 2015

    @JsonFreeman
    Contributor

    It's not really a dupe, as #1557 was for a different design. This issue is for the decorators design being implemented now.

  10. jayphelps commented on Mar 8, 2015

    @jayphelps

    My mistake.

  11. fdecampredon commented on Mar 9, 2015

    @fdecampredon

    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

  12. 124 remaining items

  13. rbuckton commented on Sep 21, 2015

    @rbuckton
    ContributorAuthor

    Tako Little (@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.

  14. TakoLittle commented on Sep 21, 2015

    @TakoLittle

    Ron Buckton (@rbuckton) thank you so much for detailed explanation
    TS team great work!! ^^d

  15. added
    ES NextNew featurers for ECMAScript (a.k.a. ESNext)
    and removed
    ES7Relates to the ES7 Spec
    on Feb 4, 2016
  16. added
    FixedA PR has been merged for this issue
    and removed
    SpecIssues related to the TypeScript language specification
    on Feb 20, 2016
  17. omeid commented on Feb 20, 2016

    @omeid

    Where is the documentation for this? and care to link the implementation commit?

    Thanks.

  18. EisenbergEffect commented on Feb 20, 2016

    @EisenbergEffect

    Mohamed Hegazy (@mhegazy) What is the status on the implementation of the latest version of the spec. I understand there are some changes there.

  19. mhegazy commented on Feb 22, 2016

    @mhegazy
    Contributor

    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 Yehuda Katz (@wycats) on the new proposal.

  20. EisenbergEffect commented on Feb 23, 2016

    @EisenbergEffect

    Mohamed Hegazy (@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!

  21. wclr commented on Aug 27, 2016

    @wclr

    Function decoration is need of course.
    Are there also plans for decorating of other objects in the code?

  22. locked and limited conversation to collaborators on Jun 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

CommittedThe team has roadmapped this issueES NextNew featurers for ECMAScript (a.k.a. ESNext)FixedA PR has been merged for this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions