[data-reveal]{opacity:1!important;transform:none!important}
Decorators
Core

Decorators

Overview

Use these factories when you are building a decorator, rather than using an existing one such as @Get. Your callback receives the decorated target first and the arguments from the decorator call afterwards.

These are TypeScript legacy decorators: enable experimentalDecorators. They do not implement the standard JavaScript decorator context API. Examples that call Reflect.defineMetadata also need import "reflect-metadata" before use; importing the package root already loads it.

Start with MakeClassDecorator for a one-target example. Consult the combined factory table only when the same decorator must support several targets.

Name a class with a decorator

This decorator stores a label when the class is declared. target is the class constructor; name is the value passed to @Label.

import { MakeClassDecorator } from "@antelopejs/interface-core/decorators";

const labels = new WeakMap<Function, string>();
const Label = MakeClassDecorator((target: Function, name: string) => {
  labels.set(target, name);
});

@Label("users")
class UserController {}

const label = labels.get(UserController); // "users"

The factories do not run request-time behavior by themselves. Your integration decides when to read stored values or invoke a decorated method.

Import

import {
  MakeClassDecorator,
  MakePropertyDecorator,
  MakeMethodDecorator,
  MakeParameterDecorator,
} from "@antelopejs/interface-core/decorators";

Types

The module exports several utility types used throughout the decorator system:

TypeDescription
Func<A, R>Generic function type with arguments A and return type R
Class<T, A>Class constructor that creates instances of type T
ClassDecorator<C>Decorator applied to class constructors
PropertyDecoratorDecorator applied to class properties
MethodDecoratorDecorator applied to class methods and accessors
ParameterDecoratorDecorator applied to method parameters

Single-target decorator factories

MakeClassDecorator

Creates a decorator factory that targets classes. The handler receives the decorated class as its first argument, followed by any factory parameters.

import { MakeClassDecorator } from "@antelopejs/interface-core/decorators";

const Entity = MakeClassDecorator((target: Function, tableName: string) => {
  Reflect.defineMetadata("table", tableName, target);
});

@Entity("users")
class User {
  name!: string;
}

MakePropertyDecorator

Creates a decorator factory that targets properties. The handler receives the target object and property key, followed by factory parameters.

import { MakePropertyDecorator } from "@antelopejs/interface-core/decorators";

const Column = MakePropertyDecorator(
  (target: any, key: PropertyKey, columnName: string) => {
    const columns = Reflect.getOwnMetadata("columns", target) || [];
    columns.push({ key, columnName });
    Reflect.defineMetadata("columns", columns, target);
  },
);

class User {
  @Column("user_name")
  name!: string;
}

MakeMethodDecorator

Creates a decorator factory that targets methods and accessors. The handler receives the target object, method key, and property descriptor, followed by factory parameters.

import { MakeMethodDecorator } from "@antelopejs/interface-core/decorators";

const Log = MakeMethodDecorator(
  (
    target: any,
    key: PropertyKey,
    descriptor: PropertyDescriptor,
    level: string,
  ) => {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      console.log(`[${level}] Calling ${String(key)}`);
      return original.apply(this, args);
    };
  },
);

class Service {
  @Log("info")
  process() {
    // ...
  }
}

MakeParameterDecorator

Creates a decorator factory that targets method parameters. The handler receives the target object, method key, and parameter index, followed by factory parameters.

import { MakeParameterDecorator } from "@antelopejs/interface-core/decorators";

const Inject = MakeParameterDecorator(
  (target: any, key: PropertyKey, index: number, token: string) => {
    const injections = Reflect.getOwnMetadata("injections", target, key) || [];
    injections[index] = token;
    Reflect.defineMetadata("injections", injections, target, key);
  },
);

class Controller {
  handle(@Inject("db") db: any) {
    // ...
  }
}

Multi-target decorator factories

For decorators that apply to multiple targets, the module provides combined factory functions. These detect the decorator context automatically based on the number and types of arguments received.

FactoryTargets
MakePropertyAndClassDecoratorProperties and classes
MakeMethodAndClassDecoratorMethods and classes
MakeMethodAndPropertyDecoratorMethods and properties
MakeMethodAndPropertyAndClassDecoratorMethods, properties, and classes
MakeParameterAndClassDecoratorParameters and classes
MakeParameterAndPropertyDecoratorParameters and properties
MakeParameterAndPropertyAndClassDecoratorParameters, properties, and classes
MakeParameterAndMethodDecoratorParameters and methods
MakeParameterAndMethodAndClassDecoratorParameters, methods, and classes
MakeParameterAndMethodAndPropertyDecoratorParameters, methods, and properties
MakeParameterAndMethodAndPropertyAndClassDecoratorParameters, methods, properties, classes

Example: method and class decorator

import { MakeMethodAndClassDecorator } from "@antelopejs/interface-core/decorators";

const Track = MakeMethodAndClassDecorator(
  (
    target: any,
    key: PropertyKey | undefined,
    descriptor: PropertyDescriptor | undefined,
    category: string,
  ) => {
    if (descriptor) {
      // Applied to a method
      const original = descriptor.value;
      descriptor.value = function (...args: any[]) {
        console.log(`[${category}] ${String(key)} called`);
        return original.apply(this, args);
      };
    } else {
      // Applied to a class
      Reflect.defineMetadata("trackCategory", category, target);
    }
  },
);

@Track("api")
class ApiController {
  @Track("endpoint")
  getUsers() {
    // ...
  }
}

When applied to a class, the key and descriptor arguments are undefined. When applied to a method, all three arguments are provided.

Next steps

  • Metadata - Reflection-based metadata with GetMetadata
  • Proxies - Module-aware proxy classes