← Back to TypeScript Course | Chapter 10: Decorators | Lesson 6 of 7

Decorator Factories

A decorator factory is a function that accepts configuration and returns a decorator. Factories make decorators reusable because each use can supply different labels, limits, prefixes, or other options.

Creating a Decorator Factory

A decorator factory is just a regular function that returns a decorator, which lets you call it with arguments like @Log(info) instead of being stuck with one fixed, argument-less @Log.

Example: Creating a Decorator Factory

typescript
function Log(level: string) {
  return function (target: any, context: ClassMethodDecoratorContext) {
    console.log(`[${level}] decorating ${String(context.name)}`);
  };
}
class Service {
  @Log("info")
  run() {}
}
new Service();

Factories with Numeric Options

Passing a numeric option — such as a maximum retry count or a cache duration in milliseconds — into the factory lets the same underlying decorator logic behave differently depending on how it's invoked on each class member.

Example: Factories with Numeric Options

typescript
function retry(maxAttempts: number) {
  return function (target: any, context: ClassMethodDecoratorContext) {
    console.log(`Will retry up to ${maxAttempts} times`);
  };
}
class Api {
  @retry(3)
  fetchData() {}
}
new Api();

Factories with Multiple Options

A factory can accept several options at once, commonly through a single configuration object argument, so callers can opt into just the settings they care about instead of remembering a long positional argument list.

Example: Factories with Multiple Options

typescript
interface Options { level: string; prefix: string; }
function configurableLog(options: Options) {
  return function (target: any, context: ClassMethodDecoratorContext) {
    console.log(`[${options.level}] ${options.prefix}: ${String(context.name)}`);
  };
}
class Service {
  @configurableLog({ level: "debug", prefix: "svc" })
  run() {}
}
new Service();

Reusable Validation Factories

Building a factory around a validation rule (like a regex or a range) turns one decorator into a reusable library you can apply across many different classes with different constraints, rather than writing bespoke validation each time.

Example: Reusable Validation Factories

typescript
function matches(pattern: RegExp) {
  return function (value: undefined, context: ClassFieldDecoratorContext) {
    return function (this: any, initial: string) {
      if (!pattern.test(initial)) throw new Error("Invalid value");
      return initial;
    };
  };
}
class User {
  @matches(/^[a-z]+$/)
  username = "ravi";
}
console.log(new User().username);

Factory Evaluation

The factory function itself runs immediately at decoration time to produce the actual decorator, while the decorator it returns only runs later when the class is defined — mixing up these two evaluation moments is a common source of bugs.

Example: Factory Evaluation

typescript
function factory(label: string) {
  console.log("Factory running now, at decoration time:", label);
  return function (target: any, context: ClassMethodDecoratorContext) {
    console.log("Decorator running later, when class is defined");
  };
}
class Example {
  @factory("early")
  method() {}
}
new Example();
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.