TypeScript Decorators & Metadata Complete Guide

Zaheer Ahmad 5 min read min read
Python
TypeScript Decorators & Metadata Complete Guide

Introduction

TypeScript decorators and metadata are powerful advanced features that allow developers to add extra behavior to classes, methods, properties, and parameters in a clean and reusable way. In simple terms, decorators are special functions that “wrap” or enhance your code, while metadata allows you to store and retrieve additional information about your code at runtime.

For Pakistani students aiming to build scalable applications—especially using frameworks like NestJS or Angular—understanding decorators is essential. Whether you're building a backend API for a Lahore-based startup or a student management system for a university in Islamabad, decorators help you write cleaner, more maintainable, and professional-grade code.

Prerequisites

Before diving into this advanced topic, you should be comfortable with:

  • Basic TypeScript syntax (types, interfaces, classes)
  • ES6+ JavaScript concepts (classes, arrow functions)
  • Object-Oriented Programming (OOP)
  • Node.js fundamentals
  • npm package installation (we will use reflect-metadata)

Core Concepts & Explanation

What Are TypeScript Decorators?

A decorator is a special kind of declaration that can be attached to a class, method, property, or parameter using the @ symbol.

Decorators are functions that run at runtime and can modify or observe the decorated element.

Example:

function LogClass(constructor: Function) {
  console.log("Class created:", constructor.name);
}

@LogClass
class Student {
  name = "Ali";
}

Explanation line-by-line:

  • function LogClass(...) → Defines a decorator function.
  • constructor: Function → Receives the class constructor.
  • console.log(...) → Logs when the class is created.
  • @LogClass → Applies the decorator to the class.
  • class Student → The class being decorated.

Types of Decorators in TypeScript

There are five main types:

  1. Class Decorators
  2. Method Decorators
  3. Property Decorators
  4. Accessor Decorators
  5. Parameter Decorators

Example of a method decorator:

function LogMethod(target: any, key: string, descriptor: PropertyDescriptor) {
  console.log("Method name:", key);
}

class Course {
  @LogMethod
  startCourse() {
    console.log("Course started");
  }
}

Explanation line-by-line:

  • target → The prototype of the class.
  • key → Method name (startCourse).
  • descriptor → Property descriptor for method.
  • @LogMethod → Decorator applied to method.
  • startCourse() → Method being decorated.

What Is Metadata in TypeScript?

Metadata means data about data. In TypeScript, metadata is often used with decorators to store additional information about classes or methods.

We use the reflect-metadata library for this.

Install it:

npm install reflect-metadata

Enable in tsconfig.json:

{
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
}

Example:

import "reflect-metadata";

class Teacher {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

With metadata enabled, TypeScript automatically stores type information.


Using reflect-metadata

import "reflect-metadata";

function LogType(target: any, key: string) {
  const type = Reflect.getMetadata("design:type", target, key);
  console.log(`${key} type:`, type.name);
}

class Student {
  @LogType
  age: number;
}

Explanation line-by-line:

  • Reflect.getMetadata(...) → Reads metadata.
  • "design:type" → Key for property type.
  • target, key → Target object and property name.
  • type.name → Displays the type (Number).
  • @LogType → Applies decorator to property.


Practical Code Examples

Example 1: Logging Decorator

function Logger(message: string) {
  return function (constructor: Function) {
    console.log(message);
    console.log(constructor.name);
  };
}

@Logger("Creating Student Class")
class Student {
  constructor(public name: string) {}
}

Explanation line-by-line:

  • Logger(message: string) → Decorator factory (takes argument).
  • return function(...) → Returns actual decorator.
  • console.log(message) → Prints custom message.
  • constructor.name → Prints class name.
  • @Logger(...) → Applies decorator with argument.
  • class Student → Target class.

Example 2: Real-World Application (Validation System)

Imagine a system in Karachi where a student registration form must validate input.

import "reflect-metadata";

const requiredMetadataKey = Symbol("required");

function Required(target: any, propertyKey: string) {
  let existingRequired =
    Reflect.getMetadata(requiredMetadataKey, target) || [];
  existingRequired.push(propertyKey);
  Reflect.defineMetadata(requiredMetadataKey, existingRequired, target);
}

function validate(obj: any) {
  const requiredFields =
    Reflect.getMetadata(requiredMetadataKey, obj) || [];

  for (let field of requiredFields) {
    if (!obj[field]) {
      console.log(`Missing required field: ${field}`);
      return false;
    }
  }
  return true;
}

class StudentForm {
  @Required
  name: string;

  @Required
  city: string;

  constructor(name: string, city: string) {
    this.name = name;
    this.city = city;
  }
}

const form = new StudentForm("Ahmad", "");
validate(form);

Explanation line-by-line:

  • requiredMetadataKey → Unique key for metadata storage.
  • @Required → Marks fields as required.
  • Reflect.getMetadata(...) → Reads existing metadata.
  • Reflect.defineMetadata(...) → Saves updated metadata.
  • validate(obj) → Checks required fields.
  • for (let field...) → Iterates fields.
  • if (!obj[field]) → Validates empty values.
  • StudentForm → Example class.
  • validate(form) → Runs validation.


Common Mistakes & How to Avoid Them

Mistake 1: Forgetting to Enable Decorators

Problem:
Code won’t compile or decorators won’t work.

Fix:

{
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
}

Always enable both options in tsconfig.json.


Mistake 2: Not Importing reflect-metadata

Problem:
Metadata functions don’t work.

Fix:

import "reflect-metadata";

This must be imported once at the entry point of your app.



Practice Exercises

Exercise 1: Create a Class Logger

Problem:
Create a decorator that logs "User created" whenever a class is instantiated.

Solution:

function LogCreation(constructor: Function) {
  console.log("User created");
}

@LogCreation
class User {
  name = "Fatima";
}

Explanation:

  • LogCreation logs message.
  • Applied using @LogCreation.
  • Runs when class is defined.

Exercise 2: Property Type Logger

Problem:
Log the type of a property using metadata.

Solution:

import "reflect-metadata";

function TypeLogger(target: any, key: string) {
  const type = Reflect.getMetadata("design:type", target, key);
  console.log(`${key} type is ${type.name}`);
}

class Product {
  @TypeLogger
  price: number;
}

Explanation:

  • Reflect.getMetadata retrieves type.
  • Logs property type.
  • Works because metadata is enabled.

Frequently Asked Questions

What is a TypeScript decorator?

A TypeScript decorator is a function prefixed with @ that modifies or observes a class, method, property, or parameter. It runs at runtime and helps add reusable behavior.

How do I enable decorators in TypeScript?

You must enable "experimentalDecorators": true and "emitDecoratorMetadata": true in your tsconfig.json file.

What is reflect-metadata used for?

It is a library that allows storing and retrieving metadata at runtime, often used with decorators for advanced features like dependency injection.

Are decorators used in real-world frameworks?

Yes, frameworks like NestJS and Angular heavily rely on decorators for routing, dependency injection, and configuration.

Can I create custom decorators?

Absolutely. You can create class, method, property, or parameter decorators to suit your application needs.


Summary & Key Takeaways

  • Decorators enhance classes, methods, and properties using @ syntax
  • Metadata allows storing extra information about code at runtime
  • reflect-metadata is essential for advanced decorator usage
  • Always enable decorators in tsconfig.json
  • Widely used in modern frameworks like NestJS
  • Helps build scalable, maintainable applications

To deepen your understanding, explore these related tutorials on theiqra.edu.pk:

  • Learn Advanced TypeScript concepts to master generics, utility types, and design patterns
  • Explore Node.js backend development for building scalable APIs
  • Dive into Java Spring Boot to compare decorator-style programming with annotations
  • Understand Microservices Architecture for real-world system design

By mastering decorators and metadata, you’re stepping into professional-level TypeScript development—something highly valuable in Pakistan’s growing tech industry. 🚀

Practice the code examples from this tutorial
Open Compiler
Share this tutorial:

Test Your Python Knowledge!

Finished reading? Take a quick quiz to see how much you've learned from this tutorial.

Start Python Quiz

About Zaheer Ahmad