Expert guidance on TypeScript decorators including class, method, property, accessor, and parameter decorators with practical examples and patterns.
Expert guidance for working with TypeScript decorators (Stage 2 experimental and Stage 3 standard). Covers class decorators, method decorators, accessor decorators, property decorators, and parameter decorators with practical implementation patterns.
Before using decorators, ensure the TypeScript compiler is configured correctly:
**For Stage 2 (experimental) decorators:**
```json
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
```
**For Stage 3 (standard) decorators (TypeScript 5.0+):**
```json
{
"compilerOptions": {
"target": "ES2022"
}
}
```
When a user asks about TypeScript decorators, follow these steps:
Determine which type of decorator the user needs:
**Basic decorator:**
```typescript
@decoratorName
class MyClass {}
```
**Decorator factory (with parameters):**
```typescript
@decoratorFactory(arg1, arg2)
class MyClass {}
```
**Multiple decorators (evaluated top-to-bottom, executed bottom-to-top):**
```typescript
@first
@second
@third
class MyClass {}
```
#### Class Decorator Pattern
```typescript
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class BugReport {
type = "report";
title: string;
constructor(t: string) {
this.title = t;
}
}
```
#### Method Decorator Pattern
```typescript
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value;
};
}
class Greeter {
@enumerable(false)
greet() {
return "Hello!";
}
}
```
#### Accessor Decorator Pattern
```typescript
function configurable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
@configurable(false)
get x() {
return this._x;
}
set x(value: number) {
this._x = value;
}
}
```
#### Property Decorator Pattern
```typescript
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value: string;
const getter = function() {
return value;
};
const setter = function(newVal: string) {
value = formatString.replace('%s', newVal);
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class Greeter {
@format("Hello, %s")
greeting: string;
}
```
#### Parameter Decorator Pattern
```typescript
function required(target: Object, propertyKey: string, parameterIndex: number) {
// Store metadata about required parameters
const existingRequiredParameters: number[] =
Reflect.getOwnMetadata("required", target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata("required", existingRequiredParameters, target, propertyKey);
}
class Greeter {
greet(@required name: string) {
return "Hello " + name;
}
}
```
**Logging/Debugging:**
```typescript
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with`, args);
const result = originalMethod.apply(this, args);
console.log(`Result:`, result);
return result;
};
return descriptor;
}
```
**Validation:**
```typescript
function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
if (args.some(arg => arg == null)) {
throw new Error('Null or undefined arguments not allowed');
}
return originalMethod.apply(this, args);
};
return descriptor;
}
```
**Memoization:**
```typescript
function memoize(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
const cache = new Map();
descriptor.value = function(...args: any[]) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = originalMethod.apply(this, args);
cache.set(key, result);
return result;
};
return descriptor;
}
```
When multiple decorators are applied:
1. **Expressions evaluated top-to-bottom** (decorator factories are called)
2. **Results executed bottom-to-top** (actual decorators run)
Within a class:
1. Parameter decorators → Method/Accessor/Property decorators (instance members)
2. Parameter decorators → Method/Accessor/Property decorators (static members)
3. Parameter decorators (constructor)
4. Class decorators
For advanced scenarios with `reflect-metadata`:
```typescript
import "reflect-metadata";
function metadata(key: string, value: any) {
return Reflect.metadata(key, value);
}
@metadata("role", "admin")
class User {
@metadata("format", "date")
createdAt: Date;
}
// Retrieve metadata
const role = Reflect.getMetadata("role", User);
const format = Reflect.getMetadata("format", User.prototype, "createdAt");
```
If using standard decorators, explain the differences:
Example:
```typescript
function logged<This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
const methodName = String(context.name);
return function (this: This, ...args: Args): Return {
console.log(`Calling ${methodName}`);
return target.call(this, ...args);
};
}
```
**User Request**: "How do I create a decorator to measure method execution time?"
**Response**:
```typescript
function measureTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function(...args: any[]) {
const start = performance.now();
const result = await originalMethod.apply(this, args);
const end = performance.now();
console.log(`${propertyKey} took ${end - start}ms`);
return result;
};
return descriptor;
}
class DataService {
@measureTime
async fetchData() {
// ... implementation
}
}
```
**User Request**: "How do I make a class immutable with decorators?"
**Response**:
```typescript
function immutable<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
constructor(...args: any[]) {
super(...args);
Object.freeze(this);
}
};
}
@immutable
class Config {
constructor(public apiKey: string) {}
}
const config = new Config("secret");
config.apiKey = "new"; // TypeError in strict mode
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/typescript-decorators-reference/raw