myHotTake

Tag: JavaScript features

  • How Do TypeScript Decorators Enhance Code Efficiency?

    If you find this story helpful, feel free to like or share it!


    I’m a superhero tailor, creating the perfect suit for a superhero. Each hero comes in with different needs and abilities, and it’s my job to create suits that enhance their powers. Now, in my workshop, I have a special rack of attachments and gadgets. These are like the decorators in TypeScript.

    When a hero walks in, say, with the power of flight, I might add a sleek wing attachment to their suit. This doesn’t change the hero themselves, but it modifies the suit to give them the ability to fly more efficiently or with more control. Similarly, in TypeScript, decorators are a special kind of declaration that can be attached to classes, methods, or properties to modify their behavior without altering their core.

    One day, an invisible hero visits me. They need a suit that enhances their stealth abilities. I add a sound-canceling gadget and a light-bending fabric to their outfit. These attachments don’t change who they are—they’re still the invisible hero—but they enhance their capabilities. In TypeScript, I use decorators in much the same way: I can attach a decorator to a method to log its calls without changing the logic inside the method itself.

    As the heroes leave my workshop with their enhanced suits, they are more capable in their missions, just like how TypeScript classes become more powerful with decorators. I love being the superhero tailor, because just like decorators, I get to enhance and modify without changing the core identity of my heroes. If you enjoyed this analogy, feel free to give it a thumbs up or share it with others who might appreciate a superhero twist on TypeScript decorators!


    For example, let’s say I have a superhero class:

    class Superhero {
      name: string;
    
      constructor(name: string) {
        this.name = name;
      }
    
      fightCrime() {
        console.log(`${this.name} is fighting crime!`);
      }
    }

    Now, just like I’d add a gadget to a superhero’s suit, I can add a decorator to the fightCrime method to log when it’s called:

    function logCall(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
      const originalMethod = descriptor.value;
    
      descriptor.value = function (...args: any[]) {
        console.log(`Calling ${propertyKey}`);
        return originalMethod.apply(this, args);
      };
    
      return descriptor;
    }
    
    class Superhero {
      name: string;
    
      constructor(name: string) {
        this.name = name;
      }
    
      @logCall
      fightCrime() {
        console.log(`${this.name} is fighting crime!`);
      }
    }
    
    const hero = new Superhero("Shadow");
    hero.fightCrime();

    In this code, the @logCall decorator is like the light-bending fabric or sound-canceling gadget. It doesn’t change the fightCrime method but modifies its behavior to log a message whenever it’s called.

    Key Takeaways:

    1. Decorators Enhance, Not Change: Just like superhero suit gadgets, decorators allow us to enhance the behavior of classes and methods without altering their fundamental logic.
    2. Code Reusability: Decorators help in writing reusable code by isolating cross-cutting concerns, such as logging, authorization, or caching, making it easy to apply them across multiple methods or classes.
    3. Clean and Maintainable Code: By using decorators, we can keep our codebase clean and maintainable by separating concerns and not cluttering the core logic with additional functionality.