1. Decorator

1.1. What are decorators?

  • Proposed feature for JavaScript
  • Declarative programming
  • Implemented as function
  • Can attached to: Properties, Parameters, Accessors, Methods, Classes

1.2. Decorator syntax

1.2.1. Simple function decorator

function uiElement(target: Function) {
  // do ui stuff
}

function deprecated(t: any, p: string, d: PropertyDescriptor) {
  console.log('This method will go away soon.');
}

@uiElement
class ContactForm {
  @deprecated
  someOldMethod() {}
}

1.2.2. decorator factory

function uiElement(element: string) {
  return function(target: Function) {
    console.log(`Creating new element: ${element}`);
  };
}

@uiElement('SimpleContactForm')
class ContactForm {
  @deprecated
  someOldMethod() {}
}

1.2.3. Class Decorator

  • Class constructor will be passed as parameter to decorator
  • Constructor is replaced if there is a return value (TFunction)
  • Return void if constructor is not to be replaced
<TFunction extends Function>(target: TFunction) => TFunction | void;

Creating class decorators that replace constructor functions

Property Decorators:

  • First parameter target is either constructor function or class prototype
  • Second parameter propertyKey is the name of the decorated member
function MyPropertyDecorator(target: Object, propertyKey: string) {
  // do decorator stuff
}

Parameter Decorators:

  • First parameter is either constructor function or class prototype
  • Second parameter is the name of the decorated member
  • Third parameter is the ordinal index of the decorated parameter
function MyParameterDecorator(target: Object, propertyKey: string, parameterIndex: number) {
  // do decorator stuff
}

Property Descriptors:

  • Object that describes a property and how it can be manipulated
interface PropertyDescriptor {
  configurable?: boolean;
  enumerable?: boolean;
  value?: any; // “value” property contains the function definition for class methods
  writable?: boolean; // “writable” property specifies if “value” is rea
  get?(): any;
  set?(v: any): void;
}

Method and Accessor Decorators:

  • First parameter is either constructor function or class prototype
  • Second parameter is the name of the decorated member
  • Third parameter is the property descriptor of the decorated member
function MyMethodDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
  // do decorator stuff
}

demo:

// decorators:

export function sealed(name: string) {
  return function(target: Function): void {
    console.log(`Sealing the constructor: ${name}`);
    Object.seal(target);
    Object.seal(target.prototype);
  };
}

export function logger<TFunction extends Function>(target: TFunction): TFunction {
  let newConstructor: Function = function() {
    console.log(`Creating new instance.`);
    console.log(target);
  };
  newConstructor.prototype = Object.create(target.prototype);
  newConstructor.prototype.constructor = target;
  return <TFunction>newConstructor;
}

export function writable(isWritable: boolean) {
  return function(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(`Setting ${propertyKey}.`);
    descriptor.writable = isWritable;
  };
}

// usage:

@logger
@sealed('UniversityLibrarian')
export class UniversityLibrarian implements Interfaces.Librarian, Employee, Researcher {
  name: string;
  email: string;
  department: string;

  assistCustomer(customerName: string) {
    console.log(this.name + ' is assisting ' + customerName);
  }

  @writable(true)
  assistFaculty() {
    console.log('Assisting faculty.');
  }

  // implementation of the following to be provided by the mixing function
  title: string;
  addToSchedule: () => void;
  logTitle: () => void;
  doResearch: (topic: string) => void;
}

@logger
export class PublicLibrarian implements Interfaces.Librarian {
  name: string;
  email: string;
  department: string;

  assistCustomer(customerName: string) {
    console.log('Assisting customer.');
  }

  @writable(false)
  teachCommunity() {
    console.log('Teaching community.');
  }
}

// test decorator:

let lib1 = new UniversityLibrarian();
let lib2 = new PublicLibrarian();

try {
  lib1.assistFaculty = () => console.log('assistFaculty replacement method');
  lib2.teachCommunity = () => console.log('teachCommunity replacement method');
} catch (error) {
  console.log(error.message);
}

lib1.assistFaculty();
lib2.teachCommunity();
Copyright © Guanghui Wang all right reserved,powered by GitbookFile Modified: 2019-08-25 13:56:34

results matching ""

    No results matching ""