Proposal to add Decorators to ECMAScript.
For the TypeScript specific proposal, see http://rbuckton.github.io/reflectdecorators/typescript.html
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 {
}
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...
}
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...
}
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...
}
}
A decorator can be legally applied to any of the following:
Please note that a decorator currently cannot be legally applied to any of the following:
This list may change in the future.
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)
When the abstract operation Decorate is called with ECMAScript language value Decorators, Object O, property key P, and property descriptor desc, the following steps are taken:
When the abstract operation DecorateConstructor is called with ECMAScript language value Decorators and Object F, the following steps are taken:
When the abstract operation DecorateProperty is called with ECMAScript language value Decorators, Object O, property key P, and property descriptor desc, the following steps are taken:
When the abstract operation CreateListFromIterator is called with ECMAScript language value iterator, the following steps are taken:
When the abstract operation GetOrCreateMetadataMap is called with Object O, property key P, and Boolean Create the following steps are taken:
"get"
, P)."set"
, P, metadataMap).When the [[HasMetadata]] internal method of O is called with ECMAScript language value MetadataKey and property key P, the following steps are taken:
When the abstract operation OrdinaryHasMetadata is called with ECMAScript language value MetadataKey, Object O, and property key P, the following steps are taken:
When the [[HasOwnMetadata]] internal method of O is called with ECMAScript language value MetadataKey and property key P, the following steps are taken:
When the abstract operation OrdinaryHasOwnMetadata is called with ECMAScript language value MetadataKey, Object O, and property key P, the following steps are taken:
"has"
, MetadataKey).When the [[GetMatadata]] internal method of O is called with ECMAScript language value MetadataKey and property key P, the following steps are taken:
When the abstract operation OrdinaryGetMetadata is called with ECMAScript language value MetadataKey, Object O, and property key P, the following steps are taken:
When the [[GetOwnMetadata]] internal method of O is called with ECMAScript language value MetadataKey and property key P, the following steps are taken:
When the abstract operation OrdinaryGetOwnMetadata is called with ECMAScript language value MetadataKey, Object O, and property key P, the following steps are taken:
"get"
, MetadataKey).When the [[DefineOwnMetadata]] internal method of O is called with ECMAScript language value MetadataKey, ECMAScript language value MetadataValue, and property key P, the following steps are taken:
When the abstract operation OrdinaryDefineOwnProperty is called with ECMAScript language value MetadataKey, ECMAScript language value MetadataValue, Object O, and property key P, the following steps are taken:
"set"
, MetadataKey, MetadataValue).When the [[MetadataKeys]] internal method of O is called with property key P the following steps are taken:
When the abstract operation OrdinaryMetadataKeys is called with Object O and property key P the following steps are taken:
"length"
)."length"
)."has"
, key)."add"
, key)."has"
, key)."add"
, key)."length"
, k).When the [[OwnMetadataKeys]] internal method of O is called with property key P the following steps are taken:
When the abstract operation OrdinaryOwnMetadataKeys is called with Object O and property key P the following steps are taken:
"keys"
)."length"
, k, true).When the [[DeleteMetadata]] internal method of O is called with ECMAScript language value MetadataKey and property key P the following steps are taken:
"delete"
, MetadataKey).This section contains amendments to the Reflect object.
A metadata decorator function is an anonymous built-in function that has [[MetadataKey]] and [[MetadataValue]] internal slots.
When a metadata decorator function F is called with arguments target and key, the following steps are taken:
When the decorator
function is called with arguments decorators, target, propertyKey, and attributes, the following steps are taken:
When the metadata
function is called with arguments metadataKey and metadataValue, the following steps are taken:
When the defineMetadata
function is called with arguments metadataKey, metadataValue, target, and propertyKey, the following steps are taken:
When the hasMetadata
function is called with arguments metadataKey, target, and propertyKey, the following steps are taken:
When the hasOwnMetadata
function is called with arguments metadataKey, target, and propertyKey, the following steps are taken:
When the getMetadata
function is called with arguments metadataKey, target, and propertyKey, the following steps are taken:
When the getOwnMetadata
function is called with arguments metadataKey, target, and propertyKey, the following steps are taken:
When the getMetadataKeys
function is called with arguments target and propertyKey, the following steps are taken:
When the getOwnMetadataKeys
function is called with arguments target and propertyKey, the following steps are taken:
When the deleteMetadata
function is called with arguments metadataKey, target, and propertyKey, the following steps are taken: