Read Anthropic’s case study about Graphite Reviewer

TypeScript decorators

Kenny DuMez
Kenny DuMez
Graphite software engineer

A TypeScript decorator is a design pattern that allows for the annotation and modification of classes or their members (methods, properties, or parameters) at design time.

Decorators are functions that are prefixed with the @ symbol and can be used to extend or alter the behavior of the class or member they are attached to, enabling meta-programming techniques such as aspect-oriented programming.

Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members, leveraging JavaScript's ability to modify runtime code behavior through functions that are called at design time during the class or member evaluation process.

In TypeScript, decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members.

Note: This guide covers experimental stage 2 decorators. For information on Stage 3 decorators see decorators in TypeScript 5.0.

Before you start using decorators, you must enable them in your TypeScript configuration. To do this set the experimentalDecorators compiler option to true in your tsconfig.json file:

Terminal
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}

TypeScript supports several types of decorators:

  • Class decorators
  • Method decorators
  • Accessor decorators
  • Property decorators
  • Parameter decorators

A class decorator is declared just before a class declaration. It is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.

Terminal
function sealed(constructor: Function) {
Object.seal(constructor)
Object.seal(constructor.prototype)
}
@sealed
class Greeter {
greeting: string
constructor(message: string) {
this.greeting = message
}
greet() {
return 'Hello, ' + this.greeting
}
}

In this example, the @sealed decorator will seal both the constructor and its prototype, preventing new properties from being added to them.

A method decorator is applied to the property descriptor of the method, and can be used to observe, modify, or replace a method definition.

Terminal
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value
}
}
class Greeter {
greeting: string
constructor(message: string) {
this.greeting = message
}
@enumerable(false)
greet() {
return 'Hello, ' + this.greeting
}
}

This @enumerable decorator modifies the enumerable property of the greet method descriptor, controlling its visibility during enumeration.

Accessor decorators are applied to the property descriptors for the accessor and can be used similarly to method decorators.

Terminal
function configurable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.configurable = value
}
}
class Greeter {
private _greeting: string
constructor(message: string) {
this._greeting = message
}
@configurable(false)
get greeting() {
return this._greeting
}
set greeting(value: string) {
this._greeting = value
}
}

Here, the @configurable decorator makes the greeting accessor non-configurable, meaning it cannot be deleted or changed.

Property decorators are used to observe, modify, or replace properties in a class.

Terminal
function format(formatString: string) {
return function (target: any, propertyKey: string) {
// property decorator logic here
}
}
class Greeter {
@format('Hello, %s')
greeting: string
constructor(message: string) {
this.greeting = message
}
}

The @format decorator in this case could be used to format the greeting property, though the implementation details are not shown.

Parameter decorators are declared before a parameter declaration and are applied to the function for a class constructor or method declaration.

Terminal
function required(
target: Object,
propertyKey: string | symbol,
parameterIndex: number
) {
// parameter decorator logic here
}
class Greeter {
greet(@required name: string) {
return 'Hello, ' + name
}
}

The @required decorator can be used to ensure that a parameter is provided when the method is called.

For further reading on TypeScript decorators see the official documentation.

Graphite
Git stacked on GitHub

Stacked pull requests are easier to read, easier to write, and easier to manage.
Teams that stack ship better software, faster.

Or install our CLI.
Product Screenshot 1
Product Screenshot 2