What are Decorators in TypeScript?
Learn what decorators are in TypeScript, how method and class decorators work, the experimentalDecorators flag, and real framework examples like Angular.
Expected Interview Answer
A decorator is a special function prefixed with `@`, applied to a class, method, property, or parameter, that can observe, modify, or replace the thing it decorates at the point the class is defined — commonly used for cross-cutting concerns like logging, validation, or dependency injection.
A method decorator like `@log` receives the target, the method name, and the property descriptor, and can wrap the original function to add behavior before returning a new descriptor. Decorators run once, at class definition time, not on every method call — the wrapping logic they install is what runs on each call afterward. Frameworks like Angular and NestJS lean heavily on decorators (`@Component`, `@Injectable`) to attach metadata that their dependency-injection systems read via `reflect-metadata`. Historically decorators required `experimentalDecorators` in `tsconfig.json` and followed the legacy stage-2 proposal; TypeScript 5.0 added support for the finalized ECMAScript decorators proposal, which has a different (simpler) signature, so code must target one version consistently.
- Separates cross-cutting concerns (logging, auth, caching) from business logic
- Powers dependency-injection metadata in Angular and NestJS
- Runs once at definition time, keeping per-call overhead to the wrapped logic only
- Composable — multiple decorators can stack on the same target
- TypeScript 5.0+ supports the standardized ECMAScript decorators proposal
AI Mentor Explanation
A decorator is like a groundsman treating a pitch before the match starts, adding extra bounce or grip once during preparation rather than intervening on every single ball bowled. That one-time preparation step then quietly affects how every delivery behaves for the rest of the day's play, the same way a decorator wraps a method once at definition and the wrapping runs on every subsequent call.
Step-by-Step Explanation
Step 1
Enable decorators
Set `"experimentalDecorators": true` in `tsconfig.json` for the legacy stage-2 style, or use TypeScript 5.0+ for the standardized proposal without the flag.
Step 2
Write the decorator function
A method decorator receives `(target, propertyKey, descriptor)` and can replace `descriptor.value` with a wrapped function.
Step 3
Apply with @ syntax
Prefix the class member with `@log` to attach the decorator, e.g. `class Api { @log greet() {...} }`.
Step 4
Runs once at definition
The decorator executes when the class is defined (module load time), not on every call — it installs the wrapped behavior.
Step 5
Stack multiple decorators
Several decorators can be applied to the same target; they compose in a defined (bottom-up for method decorators) order.
What Interviewer Expects
- Explains decorators run once at class definition, not per call
- Can write or read a basic method decorator signature
- Knows the `experimentalDecorators` flag and the legacy vs new proposal distinction
- Names a real framework use case (Angular `@Component`, NestJS `@Injectable`)
- Understands decorators can wrap/replace the original method's descriptor
Common Mistakes
- Assuming a decorator's function body re-runs on every method invocation
- Mixing legacy experimental decorators with the TypeScript 5.0 proposal inconsistently
- Forgetting to return the modified descriptor from a method decorator
- Confusing decorators with plain higher-order functions with no special syntax support
Best Answer (HR Friendly)
“Decorators are a TypeScript feature that let you attach extra behavior to a class or its methods using a simple `@` syntax, without cluttering the core logic. They're commonly used in frameworks like Angular for things like marking a class as a component or injecting dependencies automatically.”
Code Example
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with`, args);
return original.apply(this, args);
};
return descriptor;
}
class Api {
@log
greet(name: string) {
return `Hello, ${name}`;
}
}
new Api().greet('Ada'); // logs the call, then returns 'Hello, Ada'Follow-up Questions
- How does a class decorator differ from a method decorator?
- What is the difference between legacy experimentalDecorators and the TypeScript 5.0 decorators proposal?
- How do Angular and NestJS use decorators for dependency injection?
- In what order do multiple stacked decorators execute?
- What is reflect-metadata and why do some decorator-based frameworks depend on it?
MCQ Practice
1. When does a method decorator's own function body execute?
The decorator function itself runs once when the class is defined, wrapping the method; the wrapped logic then runs on each call.
2. What flag historically enabled legacy TypeScript decorators?
`experimentalDecorators` in tsconfig.json enabled the legacy stage-2 decorator proposal before TypeScript 5.0 added native support for the finalized spec.
3. Which frameworks commonly use decorators for dependency injection metadata?
Angular and NestJS use decorators like `@Component` and `@Injectable` to attach metadata their DI containers read via reflect-metadata.
Flash Cards
When does a decorator function execute? — Once, at class definition time — not on every method call.
What flag enables legacy TypeScript decorators? — `experimentalDecorators` in tsconfig.json, prior to TypeScript 5.0's native proposal support.
Name two frameworks that rely heavily on decorators. — Angular (`@Component`, `@Injectable`) and NestJS.
What does a method decorator receive as arguments? — The target, the property key (method name), and the property descriptor.