Skip to content

Instantly share code, notes, and snippets.

@rbuckton
Last active December 20, 2015 06:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rbuckton/b2d259d036224a4477f4 to your computer and use it in GitHub Desktop.
Save rbuckton/b2d259d036224a4477f4 to your computer and use it in GitHub Desktop.

Focus for decorators

  1. Decorate a Function statement?
  2. Decorate a Function expression?
  3. Decorate a Named Function expression?
  4. Decorate a Function parameter?
  5. Decorate a Class statement?
  6. Decorate a Class expression?
  7. Decorate the Class constructor?
  8. Is [7] Different from Class statement/expression?
  9. Decorate a Class member method?
  10. Decorate a Class member getter?
  11. Decorate a Class member setter?
  12. Decorate getters [10]/setters [11] seperately or together?
  13. Decorate a Class member field?
  14. Decorate a Class static method?
  15. Decorate a Class static getter?
  16. Decorate a Class static setter?
  17. Decorate a Class static field?
  18. Decorate an Object Literal method?
  19. Decorate an Object Literal getter?
  20. Decorate an Object Literal setter?
  21. Decorate an Object Literal field?

Simple Decorators

The following are decorator functions that provide minimal support for decorators using different syntax based on the kind of decorator:

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

Simple Member decorator

// - 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) {
}

Simple Parameter decorator

TBD - if added, would likely add a 3rd way to define a decorator.

Decorators with Type Metadata and Functions only

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

Metadata interfaces

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

Metadata-based function decorators

function functionDecorator(target, metadata) {
  if (metadata.kind !== "function") throw new Error("Not a function");
  ...
  return target;
}

Metadata-based member decorators

function memberDecorator(target, metadata) {
  if (!/^(static\s)?(field|method|getter|setter)$/.test(metadata.kind)) throw new Error("Not a member");
  ...
  return target;
}

Metadata-based param decorators

function paramDecorator(target, metadata) {
  if (metadata.kind !== "param") throw new Error("Not a param");
  ...
  return target;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment