Overview

I’ve been developing a prototype for a library that offers some type annotations and does post processing of instances of annotated types, and I was suprised that, in spite of the ample blog posts/articles that describes TypeScript decorators, how hard is to find some simple examples that explains both the annotation and the detection part. So here you are!

Decorator examples

The examples use the reflect-metadata library to make the examples more concice, so don’t forget to import:

import "reflect-metadata"

and the following declaration is also used in all of the examples:

const markedKey = Symbol("marked")

Class decorators

Consider the class annotation marked_class:

@marked_class
class A {
    ...
}

A simple decorator for the annotation:

function marked_class<T>(
    constructor: T /* constructor of the class */
  ) {
    Reflect.defineMetadata(markedKey, "marked with default value", constructor)
}

or if you also want to pass parameters:

@marked_class("my value")
class A {
    ...
}

a decorator factory can be developed:

function marked_class<T>(value: string){
    return function (
        constructor: T /* constructor of the class */
    ) {
        Reflect.defineMetadata(markedKey, value, constructor)
    }
}

And this is how to check if an object is instance of an annotated class:

function getClassMark(obj: any) {
    return Reflect.getMetadata(markedKey, obj.constructor)
}

Method decorators

Consider the method annotation marked_method:

class A {
    @marked_method
    doA(){...}
}

A simple decorator for the annotation:

function marked_method<T>(
    target: T, /* prototype of the class or constructor function of the class for a static member */
    propertyKey: string,
    descriptor: PropertyDescriptor
) {
    Reflect.defineMetadata(markedkey, "marked with default value", target, propertyKey)
}

if you also want to pass parameters:

class A {
    @marked_method("my value")
    doA(){...}
}

the decorator factory can be:

function marked_method<T>(value: string){
    return function (
      target: T, /* prototype of the class or constructor function of the class for a static member */
      propertyKey: string,
      descriptor: PropertyDescriptor
    ) {
        Reflect.defineMetadata(markedkey, value, target, propertyKey)
    }
}

And this is how to check if a method of an object is annotated:

function getMethodMark(obj: any, methodName: string) {
    return Reflect.getMetadata(markedKey, obj, methodName)
}

Property decorators

Consider the property annotation marked_property:

class A {
    @marked_property
    propertyA = ...
}

A simple decorator for the annotation:

function marked_method<T>(
    target: T, /* prototype of the class or constructor function of the class for a static member */
    propertyKey: string
) {
    Reflect.defineMetadata(markedkey, "marked with default value", target, propertyKey)
}

if you also want to pass parameters:

class A {
    @marked_property("my value")
    propertyA = ...
}

the decorator factory can be:

function marked_method<T>(value: string){
    return function (
        target: T, /* prototype of the class or constructor function of the class for a static member */
        propertyKey: string
    ) {
        Reflect.defineMetadata(markedkey, value, target, propertyKey)
    }
}

And this is how to check if a property of an object is annotated:

function getPropertyMark(obj: any, propertyName: string) {
    return Reflect.getMetadata(markedKey, obj, propertyName)
}