# Decorators Proposal - TypeScript

# Introduction

Proposal to add Decorators to TypeScript.

For the ECMAScript specific proposal, see http://rbuckton.github.io/reflectdecorators/index.html

# Terms

# Decorator

This section is non-normative.

A decorator is an expression that is evaluated after a class has been defined, that can be used to annotate or modify the class in some fashion. This expression must evaluate to a `function`, which is executed by the runtime to apply the decoration.

    @decoratorExpression
    class C {
    }

# Class Decorator Function

This section is non-normative.

A class decorator function is a function that accepts a constructor function as its argument, and returns either `undefined`, the provided constructor function, or a new constructor function. Returning `undefined` is equivalent to returning the provided constructor function.

    // A class decorator function
    function dec(target) {
     // modify, annotate, or replace target...
    }

# Property/Method Decorator Function

This section is non-normative.

A property decorator function is a function that accepts three arguments: The object that owns the property, the key for the property (a `string` or a `symbol`), and optionally the property descriptor of the property. The function must return either `undefined`, the provided property descriptor, or a new property descriptor. Returning `undefined` is equivalent to returning the provided property descriptor.

    // A property (or method/accessor) decorator function
    function dec(target, key, descriptor) {
      // annotate the target and key; or modify or replace the descriptor...
    }

# Parameter Decorator Function

This section is non-normative.

A parameter decorator function is a function that accepts three arguments: The function that contains the decorated parameter, the property key of the member (or `undefined` for a parameter of the constructor), and the ordinal index of the parameter. The return value of this decorator is ignored.

    // A parameter decorator
    function dec(target, paramIndex) {
        // annotate the target and index
    }

# Decorator Factory

This section is non-normative.

A decorator factory is a function that can accept any number of arguments, and must return one of the above types of decorator function.

    // a class decorator factory function
    function dec(x, y) {
      // the class decorator function
      return function (target) {
          // modify, annotate, or replace target...
      }
    }

# Decorator Targets

This section is non-normative.

A decorator can be legally applied to any of the following:

- A class declaration
- A class property initializer (static or prototype)
- A class method declaration (static or prototype)
- A class get or set accessor declaration (static or prototype)
- A parameter of a class constructor
- A parameter of a class method (static or prototype)
- A parameter of a class get or set accessor (static or prototype)

Please note that a decorator currently cannot be legally applied to any of the following:

- A class constructor - This is to reduce ambiguity between where you can apply a decorator (on the class or on its constructor) and which of the above decorator function forms is called.
- A function declaration - Decorators on a function declaration would introduce a TDZ (Temporal Dead Zone), which would make the function unreachable until its declaration is executed. This could cause confusion as an undecorated function declaration is hoisted and can be used in a statement preceeding the declaration.
- A function expression - This is to reduce confusion and maintain parity with disallowing decorators on a function declaration.
- An arrow function - This is to reduce confusion and maintain parity with disallowing decorators on a function expression.

This list may change in the future.

# Decorator Evaluation and Application Order

This section is non-normative.

Decorators are evaluated in the order they appear preceeding their target declaration, to preserve side-effects due to evaluation order. Decorators are applied to their target declaration in reverse order, starting with the decorator closest to the declaration. This behavior is specified to preserve the expected behavior of decorators without a declarative syntax.

    @F
    @G
    class C {
    }

For example, the above listing could be approximately written without decorators in the following fashion:

    C = F(G(C))

In the above example, the expression `F` is evaluated first, followed by the expression `G`. `G` is then called with the constructor function as its argument, followed by calling `F` with the result. The actual process of applying decorators is more complex than the above example however, though you may still imperatively apply decorators with a reflection API.

If a class declaration has decorators on both the class and any of its members or parameters, the decorators are applied using the following pseudocode:

    for each member M of class C
      if M is an accessor then
          let accessor = first accessor (get or set, in declaration order) of M
          let memberDecorators = decorators of accessor
          for each parameter of accessor
              let paramDecorators = decorators of parameter
              let paramIndex = ordinal index of parameter
              Reflect.decorate(paramDecorators, accessor, paramIndex)
          next parameter

          let accessor = second accessor (get or set, in declaration order) of M
          if accessor then
              let memberDecorators = memberDecorators + decorators of accessor
              for each parameter of accessor
                  let paramDecorators = decorators of parameter
                  let paramIndex = ordinal index of parameter
                  Reflect.decorate(paramDecorators, accessor, paramIndex)
              next parameter
          end if
      else if M is a method
          let memberDecorators = decorators of M
          for each parameter of M
              let paramDecorators = decorators of parameter
              let paramIndex = ordinal index of parameter
              Reflect.decorate(paramDecorators, M, paramIndex)
          next parameter
      else
          let memberDecorators = decorators of M
      end if

      let name = name of M
      let target = C.prototype if M is on the prototype; otherwise, C if M is static
      Reflect.decorate(memberDecorators, C, name)
    next member

    for each parameter of C
      let paramDecorators = decorators of parameter
      let paramIndex = ordinal index of parameter
      Reflect.decorate(paramDecorators, C, paramIndex)
    next parameter

    let classDecorators = decorators of C
    let C = Reflect.decorate(classDecorators, C)

# Reflect API

This section is non-normative.

In addition to a declarative approach to defining decorators, it is necessary to also include an imperative API capable of applying decorators, as well as defining, reflecting over, and removing decorator metadata from an object, property, or parameter.

A shim for this API can be found here: https://github.com/rbuckton/ReflectDecorators

    Reflect.decorate(decorators, target, propertyKey?, descriptor?)

# Transformation

The following are examples of how decorators can be desugared to ES6 (through a transpiler such as TypeScript). These examples levarage an imperative reflection API.

# Class Declaration

## Syntax

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

## ES6 Transformation

    let C = class {
    }
    Object.defineProperty(C, "name", { value: "C", configurable: true });
    C = __decorate([F("color"), G], C);

# Class Declaration (Exported)

## Syntax

    @F("color")
    @G
    export class C {
    }

## ES6 Transformation

    export let C = class {
    }
    Object.defineProperty(C, "name", { value: "C", configurable: true });
    C = __decorate([F("color"), G], C);

# Class Declaration (Default, Exported)

## Syntax

    @F("color")
    @G
    export default class C {
    }

## ES6 Transformation

    let C = class {
    }
    Object.defineProperty(C, "name", { value: "C", configurable: true });
    C = __decorate([F("color"), G], C);
    export default C;

# Class Method Declaration

## Syntax

    class C {
        @F("color")
        @G
        method() { }
    }

## ES6 Transformation

    class C {
        method() { }
    }
    Object.defineProperty(C.prototype, "method",
        __decorate([F("color"), G], C.prototype, "method", Object.getOwnPropertyDescriptor(C.prototype, "method")));

# Class Accessor Declaration

## Syntax

    class C {
        @F("color")
        @G
        get accessor() { }
        set accessor(value) { }
    }

## ES6 Transformation

    class C {
        get accessor() { }
        set accessor(value) { }
    }
    Object.defineProperty(C.prototype, "accessor",
        __decorate([F("color"), G], C.prototype, "accessor", Object.getOwnPropertyDescriptor(C.prototype, "accessor")));

# Class Property Declaration

## Syntax

    class C {
        @F("color")
        @G
        property = 1;
    }

## ES6 Transformation

    class C {
        constructor() {
            this.property = 1;
        }
    }
    __decorate([F("color"), G], C.prototype, "property");

# Class Constructor Parameter Declaration

## Syntax

    class C {
        constructor(@F("color") @G p) { }
    }

## ES6 Transformation

    class C {
        constructor(p) { }
    }
    __decorate([F("color"), G], C, void 0, 0);

# Class Method Parameter Declaration

## Syntax

    class C {
        method(@F("color") @G p) { }
    }

## ES6 Transformation

    class C {
        method(p) { }
    }
    __decorate([F("color"), G], C.prototype, "method", 0);

# Class Set Accessor Parameter Declaration

## Syntax

    class C {
        set accessor(@F("color") @G p) { }
    }

## ES6 Transformation

    class C {
        set accessor(p) { }
    }
    __decorate([F("color"), G], C.prototype, "accessor", 0);

# Grammar

# Expressions

PrimaryExpression MemberExpression[Expression] MemberExpression.IdentifierName MemberExpression TemplateLiteral SuperProperty MetaProperty new MemberExpression Arguments super[Expression] MemberExpression new NewExpression MemberExpression Arguments SuperCall CallExpression Arguments CallExpression [ Expression ] CallExpression . IdentifierName CallExpression TemplateLiteral NewExpression CallExpression

# Functions and Classes

FormalParameters [empty] FormalParameterList FunctionRestParameter FormalsList FormalsList,FunctionRestParameter FormalParameter FormalsList,FormalParameter BindingRestElement DecoratorList BindingRestElement BindingElement DecoratorList BindingElement PropertyName ( StrictFormalParameters ) { FunctionBody } GeneratorMethod get PropertyName ( ) { FunctionBody } set PropertyName ( PropertySetParameterList ) { FunctionBody } *PropertyName ( StrictFormalParameters ) { GeneratorBody } DecoratorList class BindingIdentifier ClassTail DecoratorList class ClassTail DecoratorList class BindingIdentifier ClassTail DecoratorList MethodDefinition DecoratorList static MethodDefinition DecoratorList static PropertyName Initializer ; ; DecoratorList Decorator @ LeftHandSideExpression

# Scripts and Modules

export * FromClause ; export ExportClause FromClause ; export ExportClause ; export VariableStatement export lookahead ≠ @ Declaration export default HoistableDeclaration export default lookahead ≠ @ ClassDeclaration export default lookahead ∉ { function, class, @ } AssignmentExpression DecoratorList export lookahead ≠ @ ClassDeclaration DecoratorList export default lookahead ≠ @ ClassDeclaration

# TypeScript

# TypeScript Definitions

    interface TypedPropertyDescriptor<T> {
        enumerable?: boolean;
        configurable?: boolean;
        writable?: boolean;
        value?: T;
        get?: () => T;
        set?: (value: T) => void;
    }

    type ClassDecorator = <TFunction extends Function>(target: TFunction): TFunction | void;
    type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;
    type PropertyDecorator = (target: Object, propertyKey: string | symbol): void;
    type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number): void;