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

Method Decorators

A method decorator receives the original method and a ClassMethodDecoratorContext. It can inspect the method, register initialization logic, or return a replacement method with added behavior.
Syntax
typescript
function DecoratorName(target: any, key: string, descriptor: PropertyDescriptor) {
  // wrap descriptor.value
}

class ClassName {
  @DecoratorName
  method() {}
}

Basic Method Decorator

A method decorator is a function applied just above a class method that receives the target class, method name, and property descriptor, letting it observe, modify, or replace that method's definition before the class is even instantiated.

Example: Basic Method Decorator

typescript
function bound(target: any, context: ClassMethodDecoratorContext) {
  console.log("Decorating method:", String(context.name));
}
class Widget {
  @bound
  render() {
    return "rendered";
  }
}
new Widget().render();

Logging Method Calls

Wrapping the original method inside the decorator lets you log every call's arguments and return value without touching the method's own body, which keeps cross-cutting concerns like logging separate from business logic.

Example: Logging Method Calls

typescript
function logCall(target: any, context: ClassMethodDecoratorContext) {
  return function (this: any, ...args: any[]) {
    console.log(`Calling ${String(context.name)} with`, args);
    const result = target.call(this, ...args);
    console.log("Returned", result);
    return result;
  };
}
class Calculator {
  @logCall
  add(a: number, b: number) {
    return a + b;
  }
}
new Calculator().add(2, 3);

Validating Method Arguments

By intercepting the descriptor's value function, a decorator can inspect incoming arguments and throw before the real method runs, giving you reusable validation instead of repeating checks at the top of every method.

Example: Validating Method Arguments

typescript
function positiveOnly(target: any, context: ClassMethodDecoratorContext) {
  return function (this: any, ...args: any[]) {
    if (args.some((a) => typeof a === "number" && a < 0)) {
      throw new Error("Arguments must be positive");
    }
    return target.call(this, ...args);
  };
}
class Account {
  @positiveOnly
  deposit(amount: number) {
    return amount;
  }
}
console.log(new Account().deposit(100));

Method Initializers

Some decorators go further and replace the method entirely with a new function that calls the original internally, which is how patterns like memoization or automatic retries get bolted onto existing methods.

Example: Method Initializers

typescript
function once(target: any, context: ClassMethodDecoratorContext) {
  let called = false;
  let result: any;
  return function (this: any, ...args: any[]) {
    if (!called) {
      result = target.call(this, ...args);
      called = true;
    }
    return result;
  };
}
class Loader {
  @once
  load() {
    console.log("Loading...");
    return "data";
  }
}
const loader = new Loader();
loader.load();
loader.load();

Method Decorator Factories

A decorator factory is a function that returns a method decorator, so you can pass configuration (like a log level or a retry count) into the decorator itself rather than hardcoding behavior into one fixed implementation.

Example: Method Decorator Factories

typescript
function retry(times: number) {
  return function (target: any, context: ClassMethodDecoratorContext) {
    return function (this: any, ...args: any[]) {
      console.log(`Configured to retry ${times} times`);
      return target.call(this, ...args);
    };
  };
}
class Api {
  @retry(3)
  fetchData() {
    return "data";
  }
}
new Api().fetchData();
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. Using an arrow function or losing this in the wrapper, so the original method is called with the wrong this.
  2. Forgetting to call the original method (and return its result) in a wrapping decorator, so the method silently does nothing.
  3. Putting the decorator on the class or a property instead of just above the method it should wrap.
🔒

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.