Factory Decorators

Factory (parameterized) decorators — `@dec(...)`. The decorator expression is a *call*: the factory runs first and returns the actual decorator, which then decorates the target. This is the shape almost every real framework uses — `@Controller('/api')`, `@Get('/users')`, `@Injectable`, `@Column({ type: 'text' })`.

Works when the returned decorator is a named function or a typed closure. A returned `any`-typed closure that captures the factory's arguments hits a separate capture limitation — annotate the factory's return type to route it through the typed-closure path (as `route` does below).

const routes: string[] = [];

// A class decorator factory: records a controller's route prefix.
function Controller(prefix: string): (target: any) => void {
  return function (target: any): void {
    routes.push(prefix);
    console.log("controller registered at", prefix);
  };
}

// A method decorator factory: records a route path (typed return so the
// captured `path` rides the typed-closure path).
function Get(path: string): (target: any, key: string, desc: any) => void {
  return function (target: any, key: string, desc: any): void {
    console.log("GET", path, "->", key);
  };
}

// A property decorator factory.
function Column(kind: string): (target: any, key: string) => void {
  return function (target: any, key: string): void {
    console.log("column", key, "is", kind);
  };
}

@Controller("/users")
class UserController {
  @Column("uuid")
  id: string = "";

  @Get("/")
  list(): string {
    return "all users";
  }

  @Get("/:id")
  find(): string {
    return "one user";
  }
}

console.log("--- defined; routes:", routes.join(", "), "---");
const c = new UserController();
console.log(c.list(), "/", c.find());
View source on GitHubexamples/decorators/factory_decorators.ts