Skip to main content

Types of decorators in typescript

TypeScript offers several types of decorators that can be applied to different parts of a class declaration to add metadata or modify behavior. These types include: 
  • Class Decorators:
    • Applied directly before a class declaration.
    • They receive the class constructor as their argument.
    • Can be used to observe, modify, or even replace a class definition.
TypeScript

    function sealed(constructor: Function) {
        Object.seal(constructor);
        Object.seal(constructor.prototype);
    }

    @sealed
    class Greeter {
        greeting: string;
        constructor(message: string) {
            this.greeting = message;
        }
        greet() {
            return "Hello, " + this.greeting;
        }
    }
  • Method Decorators:
    • Applied before a method declaration within a class.
    • They receive the target object (either the constructor function for static members or the prototype for instance members), the name of the method, and the property descriptor of the method.
    • Can be used to modify the behavior of a method, such as adding logging or error handling.
TypeScript

    function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            console.log(`Calling ${propertyKey} with arguments: ${JSON.stringify(args)}`);
            const result = originalMethod.apply(this, args);
            console.log(`${propertyKey} returned: ${JSON.stringify(result)}`);
            return result;
        };
        return descriptor;
    }

    class Calculator {
        @log
        add(a: number, b: number) {
            return a + b;
        }
    }
  • Accessor Decorators:
    • Applied before a getter or setter declaration.
    • Similar to method decorators, they receive the target, property key, and property descriptor.
    • Can be used to modify the behavior of property accessors.
TypeScript

    function configurable(value: boolean) {
        return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
            descriptor.configurable = value;
        };
    }

    class Point {
        private _x: number;
        constructor(x: number) {
            this._x = x;
        }

        @configurable(false)
        get x() {
            return this._x;
        }
    }
  • Property Decorators:
    • Applied before a property declaration within a class.
    • They receive the target object and the name of the property.
    • Primarily used to add metadata to a property, which can then be read at runtime using libraries like reflect-metadata.
TypeScript

    import "reflect-metadata";

    const formatMetadataKey = Symbol("format");

    function format(formatString: string) {
        return Reflect.metadata(formatMetadataKey, formatString);
    }

    function getFormat(target: any, propertyKey: string) {
        return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
    }

    class Post {
        @format("DD/MM/YYYY")
        createdAt: Date;
        constructor(createdAt: Date) {
            this.createdAt = createdAt;
        }
    }
  • Parameter Decorators:
    • Applied before a parameter in a class constructor or a method declaration.
    • They receive the target, the name of the method (or undefined for constructor parameters), and the index of the parameter in the argument list.
    • Can be used to add metadata to parameters, often for validation or dependency injection purposes.
TypeScript

    import "reflect-metadata";

    const requiredMetadataKey = Symbol("required");

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

    class GreeterWithValidation {
        greet(@required message: string) {
            return "Hello, " + message;
        }
    }