# Decorators

## Overview

The `@antelopejs/interface-core/decorators` module provides factory functions for creating type-safe TypeScript decorators. These factories handle the boilerplate of splitting decorator arguments from factory arguments, producing reusable, parameterized decorators for classes, properties, methods, and parameters.

## Import

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

## Types

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

| Type                 | Description                                                  |
| -------------------- | ------------------------------------------------------------ |
| `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                      |
| `PropertyDecorator`  | Decorator applied to class properties                        |
| `MethodDecorator`    | Decorator applied to class methods and accessors             |
| `ParameterDecorator` | Decorator 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.

```ts
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.

```ts
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.

```ts
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.

```ts
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.

| Factory                                              | Targets                                  |
| ---------------------------------------------------- | ---------------------------------------- |
| `MakePropertyAndClassDecorator`                      | Properties and classes                   |
| `MakeMethodAndClassDecorator`                        | Methods and classes                      |
| `MakeMethodAndPropertyDecorator`                     | Methods and properties                   |
| `MakeMethodAndPropertyAndClassDecorator`             | Methods, properties, and classes         |
| `MakeParameterAndClassDecorator`                     | Parameters and classes                   |
| `MakeParameterAndPropertyDecorator`                  | Parameters and properties                |
| `MakeParameterAndPropertyAndClassDecorator`          | Parameters, properties, and classes      |
| `MakeParameterAndMethodDecorator`                    | Parameters and methods                   |
| `MakeParameterAndMethodAndClassDecorator`            | Parameters, methods, and classes         |
| `MakeParameterAndMethodAndPropertyDecorator`         | Parameters, methods, and properties      |
| `MakeParameterAndMethodAndPropertyAndClassDecorator` | Parameters, methods, properties, classes |

### Example: method and class decorator

```ts
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](./4.metadata.md) - Reflection-based metadata with `GetMetadata`
- [Proxies](./2.proxies.md) - Module-aware proxy classes
