Decorators—Add Metadata and Behavior to Classes
Use decorators to attach metadata and inject reusable behavior into classes, methods, and properties in a clean, declarative way.
As developers, we often need to inject behavior, annotate structure, or enforce contracts without polluting our core logic. Think of logging, validation, dependency injection, or marking fields as required. These are cross-cutting concerns. They crop up everywhere—classes, methods, properties—repeating the same logic.
Enter decorators.
Decorators in TypeScript provide a powerful syntax to extend and annotate code behavior at runtime—while keeping business logic crisp and uncluttered. If you’ve worked with Angular or NestJS, you’ve already seen them: @Component(), @Injectable(), @Get(), and more.
This lesson introduces decorators from first principles: what they are, how to enable them, how they work under the hood, and how to apply them responsibly. We’ll walk through class, method, and property decorators using plain TypeScript—no frameworks—to clearly understand what’s happening.
Let’s get started by laying the foundation: how to work with class constructors as values.
Understanding constructor types
Decorators operate at definition time. When we apply a decorator to a class, what we’re really doing is passing the class’s constructor function to another function. To build decorators well, we need to know how to type constructor functions precisely.
In TypeScript, a constructor type looks like this: