Angular, Typescript, and Decorators
Consider using the unsung feature of decorators to power-up your angular web application
In this post, we’ll learn what decorators are, why they’re useful, and how to create them. We’ll do this with TypeScript — demonstrating how useful this is in the angular framework.
Decorators???
In truth, decorators are not a part of ES6. However, in keeping with the nature of TypeScript they’re currently an experimental feature, which can be enabled by adding:
If you need more details about the draft, head over to the TC39 proposal for decorators.
OK, and…???
Hold on! I just want to make it clear that decorators are not a part of TypeScript by default and certainly not a part of JavaScript as of yet.
A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter.
One quick point is to include emitDecoratorMetadata
for your compiler options. You would use this in conjunction with experimentalDecorators
for design-time type information to be exposed at runtime. It’s similar to Reflect in C#.
Now back to business
Decorators allow you to modify/adjust classes and functions by annotating i.e. decorating with your custom super-powered function.
They’re not a new thing to the programming world — Java and C# and other backend focused programming language already have them. The feature is similar to function overloading and overriding.
However in JavaScript, this has not been the case — and there is always excitement for new cool and shiny things!
Usefulness
If you are an Angular developer, you may have noticed that decorators are the backbone, and what has given rise to the Angular framework.
It is a “decorators + typescript = angular” sort of thing.
Angular uses decorators for many many things and even “third party” libraries like NgRx. Things you may or may not have seen include, but are not limited to:
@Component
@Injectable
@Directive
@NgModule
@Effect
Not to throw shade on vanilla JS, but it is completely doable to do the same however it is frankly painful. For example:
vs
The @Component
is the decorator piece, which modifies the class
so that it can take additional parameters such as a template
and selector
. You can also see the difference with the length, encapsulation, and wordiness.
Let’s say we have a vehicle class:
Instead of creating subclasses (most OOP would do this) to define a bike, you could create a decorator to define these!
Demo
If we want to annotate class members then we would have to annotate them individually.
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).
I want to be able to freeze any class I add my decorator too, this way it will not allow mutations to the class or its prototype. For example:
Now to apply the decorator is simply will freeze the class when it is declared. So to test this we can simply:
What about modifying what the constructor does?
If the class decorator returns a value, it will replace the class declaration with the provided constructor function.
Should you choose to return a new constructor function, you must take care to maintain the original prototype. The logic that applies decorators at runtime will not do this for you.
Let’s see how we could implement this. First, we have to adjust our decorator definition and implementation. This is how typescript.lang define theirs:
However, in our scenario we can make it slightly more readable with:
The declaration is stating a few things:
- We would like to extend the current/incoming class.
- T is extending the prototype of the current class.
When you hover over T
we’ll see:
(type parameter) T in bike<T extends new (...args: any[]) => {}>(vehicle: T): {
new (...args: any[]): (Anonymous class);
prototype: bike<any>.(Anonymous class);
} & T
As you can see, the returned declaration for T
includes our decorator.
Now to augment the current vehicle, which is easily done:
Easy, peasy, lemon squeezy!
This example was just to show how to use decorators to augment your classes. I am by no means saying that if you have a vehicle class you should use a decorator to do these things. They’re more useful for things which are done repeatedly.
Finally
Let’s try to add some parameters to make it more configurable.
Our bike will only be defined as a two-wheeler, but we could customize or add configurations for n
number of params:
This will allow the consumer of this decorator to define the properties for the class rather than closing it off. Additionally, it makes it much easier to test with multiple configs.
One scenario where we could use this is in having our own translation logic for each component.
That’s it!
Decorators can change how we design and develop our applications. They promote encapsulation and offers flexibility when needed. The next time you run into a problem involving features for multiple classes, functions or members, you have the knowledge to will help you make the decision easier!