- Decorate a Function statement?
- Decorate a Function expression?
- Decorate a Named Function expression?
- Decorate a Function parameter?
- Decorate a Class statement?
- Decorate a Class expression?
- Decorate the Class constructor?
- Is [7] Different from Class statement/expression?
- Decorate a Class member method?
- Decorate a Class member getter?
- Decorate a Class member setter?
- Decorate getters [10]/setters [11] seperately or together?
- Decorate a Class member field?
- Decorate a Class static method?
- Decorate a Class static getter?
- Decorate a Class static setter?
- Decorate a Class static field?
- Decorate an Object Literal method?
- Decorate an Object Literal getter?
- Decorate an Object Literal setter?
- Decorate an Object Literal field?
The following are decorator functions that provide minimal support for decorators using different syntax based on the kind of decorator:
// - satisfies [1], [2], [3], [5], [6].
// - [2] & [3] are hard to get right without runtime/compiler support due to how NFEs work
// - [6] & [7] can be odd if classes only work with 'new', since you could replace with a function
// that can be used with [[Call]]
// - 'name' is necessary if you need to reference the name (logging, etc) as target.name can be
// lost when nesting decorators
//
// - pro: simple
// - con: different syntax for function/class vs member decorators
// - con: hard (but not impossible) to use same decorator for function/class and member by checking
// arguments.length
// - con: no way to decorate parameters?
function functionDecorator(target: Function, name: string): Function {
...
return target;
}
// - satisfies [9], [10], [11], [13], [14], [15], [16], [17], [18], [19], [20], [21],
// and [12]=Together
//
// - pro: mirrors Object.defineProperty
// - con: different syntax for function/class vs member decorators
// - con: if fields are added in ES7 and lifted to the constructor, decorating fields
// happens at a different time than decorating other members.
// - con: how do i know if I am a static or a member? I can typeof target (a "function" for static,
// but an "object" with constructor property for a member)
function memberDecorator(target: any, name: string, descriptor: PropertyDescriptor) {
}
TBD - if added, would likely add a 3rd way to define a decorator.
Another way to define decorators could be by providing a bit of a meta-type system on top of decorators, that provides more context as to how the decorator was applied
interface DecoratorMetadata {
// - one of: "function", "class", "field", "static field", "getter", "static getter", "setter",
// "static setter", "method", "static method", "param"
kind: string;
name: string;
ref?: Function;
}
interface ParameterDecoratorMetadata extends DecoratorMetadata {
ordinal: number;
rest: boolean;
function: DecoratorMetadata;
}
interface MemberDecoratorMetadata extends DecoratorMetadata {
class: DecoratorMetadata;
}
// - satisfies [1], [2], [3], [4], [5], [6], [9], [10], [11], [13], [14], [15], [16], [17]
// [18], [19], [20], [21], and [12]=Seperate
// - pro: single syntax for decorators
// - pro: easy to determine what is being decorated
// - pro: seperates decorator application and decorator execution, which is a benefit for
// field and param decorators
// - con: one-size-fits-all may not work
// - con: adds meta-type system in form of DecoratorMetadata that is new and not tightly
// integrated into other APIs
// - con: Harder to do things like change 'writable' or 'configurable' for a member
// due to assignments
// - con: extra allocations for metadata and functions used to mutate fields/params
interface Decorator {
(target: Function, metadata: DecoratorMetadata): Function;
}
function functionDecorator(target, metadata) {
if (metadata.kind !== "function") throw new Error("Not a function");
...
return target;
}
function memberDecorator(target, metadata) {
if (!/^(static\s)?(field|method|getter|setter)$/.test(metadata.kind)) throw new Error("Not a member");
...
return target;
}
function paramDecorator(target, metadata) {
if (metadata.kind !== "param") throw new Error("Not a param");
...
return target;
}