Typescript -Decorator Pattern — A Functional Perspective

Dimitris Papadimitriou
3 min readNov 8, 2020

--

Functional composition is in the heart of many object-oriented best practices. Here we will see an idiomatic expression of the Decorator design pattern, which is one of the most used object-oriented design patterns from Gang of Four.

Let start with simple example where we have a ProductA , and we can decorate it with Packaging. Adding a packaging would affect the price, but we want to treat a product and a packaged product interchangeably in our code. The decorator pattern allows us to do just that by unifying both ProductA and Packaging under a common in interface :

Run this

The decorator pattern is based on the idea/theme that the decorator exposes the same interface with the decorated object. In this example, the Packaging exposes the same getPrice() method with the decorated Product. This allows the rest of the application to handle a simple product or a decorated product in the same way without needing to know more details in order to get the price [this is a nice display of information hiding/encapsulation in object-oriented programming, since the decorated object hides the details of implementation from the outside world and only reveals the price].

Object literal notation

In the functional realm we can achieve this by functional composition. First, we can simplify by removing the classes and move to the object literal notation:

Run This

What you want to note here is that the outline-form of this notation, corresponds to the OOP class notation:

             (price: number) => ({ getPrice: (): number => {…} });{ constructor(price: number) {…}   getPrice (): number {…} }

If you look at this carefully you will see that the (price: number) => corresponds to the constructor constructor ! Now we can write:

var createProductWithDoublePackaging: (price: number) => Product = price => Packaging(Packaging(ProductA(price)));console.log(createProductWithDoublePackaging(10).getPrice());

or use compose to make the composition

var createProductWithDoublePackaging: (price: number) => Product = compose(Packaging,Packaging,ProductA);console.log(createProductWithDoublePackaging(10).getPrice());

Only functions notation

Finally, we can strip of the literal notation completely and only keep the functions

Run This: Ts Fiddle

where we stripped all the literal brackets ({ }) and the naming of the functions. If you compare the skeletal structure outline :

Only functions  (price: number) =>              (): number =>{…}Object literals (price: number) => ({ getPrice: (): number =>{…} });OOP   {constructor(price: number) {…} getPrice  (): number {…} }

So, In a way Any function of this form (a:T) => (b:R) => {} is equivalent to an object that takes an a:T in the constructor and has only one method that takes an b:R as argument.

Conslusion:

The idea here is for the decorating composing functions that come after the decorated function that has a return type Product.

Here the type of the function Packaging is Product Product. This means that it does not affect the signature after composition, because any T→T is an Identity for the composition (_ ∘ _) operation as a monoid.

Product ∘ Packaging: number → ProductProduct : number → Product

after pre-composing number → Product with Product → Product the composition remains of type number → Product.

Excerpt from the book :

https://leanpub.com/functional-programming-in-Ts-with-categories

--

--