← 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.
Syntax
typescript
function FactoryName(option: type) {
  return function (target: any) {
    // use option
  };
}

@FactoryName(value)
class ClassName {}

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();
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting the outer call and writing @Log instead of @Log("info"), so the factory itself is used as the decorator.
  2. Returning nothing from the factory, so there is no decorator function to apply.
  3. Doing heavy work in the factory body, when it runs when the class loads rather than each call.
🔒

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.