Nicole Oliver

      @nixallover

Pattern    vs    Feature

Add behavior to an individual object, dynamically, without affecting the behavior of other objects from the same class

Add functionality to classes and class members at definition time

(in TypeScript)

A way to observe, modify, and replace class declarations and members

A way to provide annotations and a meta-programming syntax for class declarations and members

{
    /* Experimental Options */

    /* Enables experimental support for ES7 decorators. */
    "experimentalDecorators": true,

    /* Enables experimental support for emitting type metadata for decorators. */
    "emitDecoratorMetadata": true,
}
tsconfig.json

Decorators are currently  a stage 2 ECMAscript TC39 proposal

@sealed
class Raccoon {
    isFuzzy: boolean = true;
    shouldPet: boolean = false;
}
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

Defined

Applied

class Bear {
    @deprecate
    hibernate(): void {
        // ...
    }
}
function deprecate(
    target: any, 
    propertyKey: string, 
    descriptor: PropertyDescriptor
) {
    const message = 'This will be removed in future versions';
    return {
        ...descriptor,
        value: () => {
            console.warn(`DEPRECATED: ${key}: ${message}`;
            return descriptor.value.apply(this, arguments);
        }
    };
}

Defined

Applied

class Bear {
    @validate
    growl(@required volume: 1 | 11): void {
        if (volume === 1) {
            console.log('Grrraaaa');
        } else if (volume === 11) {
            console.log('GRRRRAAAAAWWHRHHF');
        }
    }
}
import "reflect-metadata";

const requiredMetadataKey = Symbol("required");

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
    let existingReqParams: number[] = Reflect.getOwnMetadata(
        requiredMetadataKey, 
        target, 
        propertyKey
    ) || [];
    existingReqParams.push(parameterIndex);
    Reflect.defineMetadata(requiredMetadataKey, existingReqParams, target, propertyKey);
}

Defined

Applied

PropertyDescriptor
configurable
enumerable
writable
value
let cub = {};

cub.name = 'Rupert';
// Defaults - created by assignment

{ 
    configurable: true, 
    enumerable: true, 
    writable: true,
    value: 'Rupert'
}

Object.defineProperty(cub, 'name', {value: 'Rupert'});
// Defaults - created with Object.defineProperty

{ 
    configurable: false, 
    enumerable: false, 
    writable: false,
    value: 'Rupert'
}

Common

Applications

  • logging
  • telemetry/performance
  • validate & verify
  • memoize
  • parameter required
  • PropertyDescriptor
    • enumerable
    • configurable
    • writable

Beware

Code smells

Resources & further reading

More On Angular decorators

Credits

Thanks!

Nicole Oliver

      @nixallover

Huge thanks to ngAtlanta organizers, Angular Seattle friends, my Nintendo coworkers, and Jude Poole for ideas and support!

Made with Slides.com