Get Started with Typescript Decorators

Understanding decorators and applying them to your Typescript projects

Decorators at a High Level

{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
tsc --target ES5 --experimentalDecorators

Where are decorators used?

// class definitions@decorator
class MyComponent extends React.Component<Props, State> {
...
// class methods@decorator
private handleFormSubmit() {
...
// class method parametersprivate handleFormSubmit(@decorator myParam: string) {
...
// accessors@decorator
public myAccessor() {
return this.privateProperty;
}
// class properties@decorator
private static api_version: string;
...
@deprecated
class MyComponent extends React.Component<Props> {
...
}

Decorators are great as logging utilities

Introducing Decorator Factories

@inject({
api_version: '0.3.4'
})
@deprecated
class MyComponent extends React.Component<Props> {
...
}
function inject(options: { api_version: string }) 
return target => {
...
}
}
@inject({
api_version: '0.3.4'
})
class MyComponent extends React.Component<Props> {
...
}
function inject(options: { api_version: string }) 
return target => {
target.apiVersion = options.api_version;
}
}
@inject({
api_version: '0.3.4'
})
class MyComponent extends React.Component<Props> {
static apiVersion: string;

...
}
function deprecated(target) {
console.log('this class is deprecated and will be removed in a future version of the app');
console.log('@: ', target);
}
@deprecated
class MyComponent extends React.Component<Props> {
...

Decorator Implementations

Classes

function classDecorator(options: any[]) {
return target => {
...
}
}
@classDecorator
class ...

Class properties

function prop(target, name) => {
...
}
}
class MyComponent extends React.Component<Props> {
@prop
public apiVersion: string
...
@prop
public static apiVersion: string;
...

Methods

function methodDecorator(options: any[]) {
return (
target: MyComponent,
propertyKey: string,
propertyDescriptor: PropertyDescriptor
) => {
...
}
}
class MyComponent extends React.Component {
...
@methodDecorator
handleSomething() {
...
}
}
function enumerable(enumerable: boolean) {
return (
target: MyComponent,
propertyKey: string,
propertyDescriptor: PropertyDescriptor
) => {
propertyDescriptor.enumerable = enumerable;
}
}
class MyComponent extends React.Component {
...
@enumerable(false)
handleSomething() {
...
}
}
function logger(
target: MyComponent,
propertyKey: string,
propertyDescriptor: PropertyDescriptor
) => {

//get original method
let originalMethod = propertyDescriptor.value;

//redefine descriptor value within own function block
propertyDescriptor.value = function (...args: any[]) {
//log arguments before original function
console.log(
`${propertyKey} method called with args:
${JSON.stringify(args)}`);
//attach original method implementation
let result = originalMethod.apply(this, args);
//log result of method
console.log(
`${propertyKey} method return value:
${JSON.stringify(result)}`);
}
}

//apply decorator to a class method
...
@logger
handleSomething(name, value) {
...
}

Method Parameters

function decorator(
class,
name: string,
index: int
) => {
...
}
class MyComponent extends React.Component<Props> {
...

private handleMethod(@decorator param1: string) {
...
}
}

Importing Decorators

import { logger, deprecated } from '../decorators';
src/
/decorators
index.ts
import { logger, deprecated } from '@myorg/decorators';

In Summary

My thoughts on decorators

@issue(850, 'good first issue')
class ProblematicClass extends React.Component<Props, State> {
...

Programmer and Author. Director @ JKRBInvestments.com. Creator of LearnChineseGrammar.com for iOS.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store