⚠️
Decorators are an experimental feature that may change in future releases.
A Decorator is a special kind of declaration that can be attached to class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

Decorator Evaluation

There is a well defined order to how decorators applied to various declarations inside of a class are applied:
  1. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each instance member.
  1. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each static member.
  1. Parameter Decorators, are applied for the constructor.
  1. Class Decorators are applied for the class.

Class Decorators

A Class Decorator is declared just before a class declaration. The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition. A class decorator cannot be used in a declaration file, or in any other ambient context (such as on a declare class).
ambient context: https://github.com/Microsoft/TypeScript-Handbook/issues/180#issuecomment-195452691
The expression for the class decorator will be called as a function at runtime, with the constructor of the decorated class as its only argument.
If the class decorator returns a value, it will replace the class declaration with the provided constructor function.
 
interface ControllerInterface { render: ( template: string, data: Record<string, any> ) => string; } function Controller<T extends { new ( ...args: any[] ): {} }>( constructor: T ) { return class extends constructor implements ControllerInterface { render( template: string, data: Record<string, string> ) { return template.replace( /{%\s*([a-zA-Z$_][a-zA-Z0-9$_]*)\s*%}/g, ( m, n ) => data[ n ] ?? '' ); } } } @Controller class HomeController { title = 'Home'; constructor( title?: string ) { title && ( this.title = title ); } indexAction() { } } const home = new HomeController(); // Property 'render' does not exist on type 'HomeController'.(2339) console.log( home.render() ); const page = new HomeController() as unknown as ControllerInterface; page.render( 'My name is {% name %}', { name : 'Achilles' } );
type UserDataDto = () => {} @Class class A { @Method index( @Param user: UserDataDto, id : number ) {} @Property x = 1; @Accessor get() { return 1 } set( n: number ) {} }
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; let A = class A { constructor() { this.x = 1; } index(user, id) { } get() { return 1; } set(n) { } }; __decorate([ Method, __param(0, Param), __metadata("design:type", Function), __metadata("design:paramtypes", [Function, Number]), __metadata("design:returntype", void 0) ], A.prototype, "index", null); __decorate([ Property, __metadata("design:type", Object) ], A.prototype, "x", void 0); __decorate([ Accessor, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], A.prototype, "get", null); A = __decorate([ Class ], A);
var __decorate = function( decorators, target, key, desc ) { const c = arguments.length; let descriptor = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor( target, key ) : desc; for( let i = decorators.length - 1; i >= 0; i-- ) { const decorator = decorators[ i ]; if( decorator = decorators[i] ) { if( c < 3 ) { descriptor = decorator( descriptor ); } else if( c > 3 ) { descriptor = decorator( target, key, descriptor ); } else { descriptor = decorator( target, key ); } } } c > 3 && descriptor && Object.defineProperty( target, key, descriptor ); return descriptor; };

Method Decorators

A Method Decorator is declared just before a method declaration. The decorator is applied to the Property Descriptor for the method, and can be used to observe, modify, or replace a method definition. A method decorator cannot be used in a declaration file, on an overload, or in any other ambient context (such as in a declare class).
 
The expression for the method decorator will be called as a function at runtime, with the following three arguments:
  1. Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
  1. The name of the member.
  1. The Property Descriptor for the member.
If the method decorator returns a value, it will be used as the Property Descriptor for the method.
 
function Response( extra?: Record<string, any> ): MethodDecorator { return ( target: any, key: string | symbol, descriptor: TypedPropertyDescriptor<any> ) => { const fn = descriptor.value; console.log( 'Order: 1' ); descriptor.value = function() { const data = fn.call( this ); return { status: 0, message : 'OK', ...extra, data } }; return descriptor; } } class HomeController { @Response( { time : +new Date } ) indexAction() { return { name : 'Achilles' } } } console.log( 'Order: 2 ); const response = new HomeController().indexAction(); console.log( response );
Output:
{ status: 0, message: 'OK', time: 1605109988561, data: { name: 'Achilles' } }

Accessor Decorators

An Accessor Decorator is declared just before an accessor declaration The accessor decorator is applied to the Property Descriptor for the accessor and can be used to observe, modify, or replace an accessor's definitions. An accessor decorator cannot be used in a declaration file, or in any other ambient context.
⚠️
Typescript disallows decorating both the get and set accessor for a single member. Instead, all decorators for the member must be applied to the first accessor specified in document order. This is because decorators apply to a Property Descriptor, which combines both the get and set accessor, not each declaration separately.
The expression for the accessor decorator will be called as a function at runtime, with the following three argument:
  1. Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
  1. The name of the member.
  1. The Property Descriptor for the member.

Property Decorators

A Property Decorator is declared just before a property declaration. A property decorator cannot be used in a declaration file, or in any other ambient context.
The expression for the property decorator will be called as a function at runtime, with the following two arguments:
  1. Either the constructor function of the class for a static member, or the property of the class for an instance member.
  1. The name of the member.
⚠️
A Property Descriptor is not provided as an argument to a property decorator due to how property decorators are initialized in TypeScript. This is because there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. The return value is ignored too. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class.
class Greeter { greeting: string = 'abc'; constructor( message: string ) { this.greeting = message; } set name( s: string ) { } get name(): string { return 'da'; } greet() { } }
"use strict"; var Greeter = /** @class */ (function () { function Greeter(message) { this.greeting = 'abc'; this.greeting = message; } Object.defineProperty(Greeter.prototype, "name", { get: function () { return 'da'; }, set: function (s) { }, enumerable: false, configurable: true }); Greeter.prototype.greet = function () { }; return Greeter; }());
We can use this information to record metadata about the property.
import 'reflect-metadata'; const formatMetadataKey = Symbol( 'format' ); function format( formatString: string ) { return Reflect.metadata( formatMetadataKey, formatString ); } function getFormat( target: any, propertyKey: string ) { return Reflect.getMetadata( formatMetadataKey, target, propertyKey ); }

Parameter Decorators

A Parameter Decorator is declared just before a parameter declaration. The parameter decorator is applied to the function for a class constructor or method declaration. A parameter decorator cannot be used in a declaration file, an overload, or in any other ambient context.
The expression for the parameter decorator will be called as a function at runtime, with the following three arguments:
  1. Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
  1. The name of the member.
  1. The ordinal index of the parameter in the function's parameter list.
The return value of the parameter decorator is ignored.
import 'reflect-metadata'; function Body(): ParameterDecorator { return ( target, key: string | symbol, index: number ) => { console.log( { target, key, index } ); } } class A { index( @Body() data: Record<string, any>, @Body() name: string, @Body() ...args: any[] ) { } }
Output:
{ target: {}, key: 'index', index: 2 } { target: {}, key: 'index', index: 1 } { target: {}, key: 'index', index: 0 }

Metadata

{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }
When enabled, as long as the reflect-metadata library has been imported, additional design-time type information will be exposed at runtime.
namespace Reflect { export declare function getMetadata( key: string, instance: any, method: string ): any[]; } class DTO { constructor( name: string ) { console.log( name ); } readonly name!: string; } function Body(): ParameterDecorator { return ( target, key: string | symbol, index: number ) => {} } class A { index( data: DTO, @Body() name: string ) { } } const a = new A(); const paramtypes = Reflect.getMetadata( 'design:paramtypes', a, 'index' ); console.log( paramtypes ); new paramtypes[ 0 ]( 'Achilles' );
badge