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!

Wrapping it up with decorators - ngAtlanta 2019

By Nicole Oliver

Wrapping it up with decorators - ngAtlanta 2019

As Angular developers we use decorators every day. Let's peek at how the core Angular decorators bring our code to life, and use that as a springboard for a discussion about creating our own magical wrappers in TypeScript.

  • 2,136