Last year, the Angular team announced it’s ECMAScript language extension AtScript, which adds types and annotations to the language in order to enable better tooling, debugging and overall development experience. Half a year later at ng-conf, the team announced that AtScript becomes TypeScript, which supports annotations and another feature called “decorators”.
But how do those annotations actually work? And what are decorators then? This article details the translation of annotations and how they differ from decorators.
We have a class
Tabs that is basically empty. The class has one annotation
@Component. If we’d remove all annotations, what would be left is just an empty class that doesn’t have any special meaning right? So it seems that
@Component add some metadata to the class in order to give it a specific meaning. This is what annotations are all about. They are a declarative way to add metadata to code.
@Component is an annotation that tells Angular, that the class, which the annotation is attached to, is a component.
Okay, even if that seems to be quite clear, there are a few questions coming up:
- Who defined this annotations called
- If this is part of AtScript, what does that translate to, so we can use it in today’s browsers?
Let’s answer these one by one. Where do those annotations come from? To answer that question, we need to complete the code sample.
@Component is something we need to import from the Angular 2 framework like this:
This pretty much answers our first question. Both annotations are provided by the framework. Let’s take a look at what the implementation of those annotations look like:
We can see that
ComponentMetadata is in fact an implementation detail of the Angular framework. This answers our second question.
But wait. It’s just yet another class? How can just a simple class change the way how other classes behave? And why are we able to use those classes as annotations by just prefixing them with an
@ sign? Well, actually we can’t. Annotations are not available in browser’s of today, which means we need to transpile it to something that does run in current browsers.
Even though we have a couple of transpilers we can choose from. Babel, Traceur, TypeScript, … It turns out there’s only one that actually implements annotations as we know them from AtScript: Traceur. Taking the component code from above, this is what it translates to using Traceur:
In the end, a class is just a function, which is also just an object, and all annotations end up as instance calls on the
annotations property of the class. When I said “all” annotations end up there, I actually lied a bit. We can have parameter annotations as well and whose will be assigned to a class’
parameters property. So if we have code like this:
This would translate to something like this:
The reason why this translate to a nested array, is because a parameter can have more than one annotation.
Okay, so now we know what those metadata annotations are and what they translate to, but we still don’t know how something like
@Component makes a normal class actually a component in Angular 2. It turns out that Angular itself takes care of that. Annotations are really just metadata added to code. That’s why
@Component is a very specific implementation detail of Angular 2. In fact, there are a couple of other annotations that the framework comes with. But also only the framework knows what to do with that information.
Another very interesting learning is that Angular expects the metadata on
parameters properties of classes. If Traceur would not translate them to those particular properties, Angular 2 wouldn’t know from where to get the metadata. Which makes AtScript Annotations just a very specific implementation of what annotations could actually be.
Wouldn’t it be nicer if you as a consumer could decide where your metadata is attached to in your code? Yes! And this is where decorators come into play.
Decorators are a proposed standard for ECMAScript 2016 by Yehuda Katz, to annotate and modify classes and properties at design time. This sounds pretty much like what annotations do right? Well… sort of. Let’s take a look at what a decorator looks like:
Wait. This looks exactly like an AtScript annotation! That’s right. But it isn’t. From a consumer perspective, a decorator indeed looks like the thing that we know as “AtScript Annotation”. There is a significant difference though. We are in charge of what our decorator does to our code. Taking the code above, a corresponding decorator implementation for
@decoratorExpression could look like this:
Right. A decorator is just a function that gives you access to the
target that needs to be decorated. Get the idea? Instead of having a transpiler that decides where your annotations go, we are in charge of defining what a specific decoration/annotation does.
This, of course, also enables us to implement a decorator that adds metadata to our code the same way AtScript annotations do (I keep referring to “AtScript annotations” because what they do, is really an AtScript specific thing). Or in other words: with decorators, we can build annotations.
There’s a lot more to explore about decorators, but that is out of the scope of this article. I recommend checking out Yehuda’s proposal to learn more about the feature.
Does TypeScript support Annotations or Decorators?
As you might know, the Angular team announced earlier this year that they’re going to drop the term “AtScript” in favour of TypeScript, since both languages seem to solve the same problems. In addition, there were announcements that TypeScript will support annotations and decorators once version 1.5 alpha is out.
It turns out that it actually doesn’t. TypeScript supports decorators, but doesn’t know about Angular 2 specific annotations. Which makes sense, because they are an implementation detail of Angular. That also means that either we as consumers, or the framework needs to provide those decorators in order to make the code compile. Only the latter really makes sense. Luckily, generators for both, annotation and parameter decorators, have landed in the Angular 2 code base lately. So what the framework behind the scenes does is, it comes with metadata annotation implementations, which are then passed to the decorator generator to make decorators out of them. That’s also why we have to write the following code when transpiling with traceur:
As we can see, we’re actually importing the metadata rather than the decorator. This is simply just because traceur doesn’t understand decorators, but does understand
@Component annotations. Which is why we’re also importing them with these namespaces respectively.
“AtScript Annotations” and decorators are nearly the same thing. From a consumer perspective we have exactly the same syntax. The only thing that differs is that we don’t have control over how AtScript annotations are added as metadata to our code. Whereas decorators are rather an interface to build something that ends up as annotation. Over a long term, however, we can just focus on decorators, since those are a real proposed standard. AtScript is deprecated, and TypeScript implements decorators.
I hope this article made some things clear though.
Get updates on new articles and trainings.
Join over 1400 other developers who get our content first.
Testing Services with Http in Angular 2
Want to learn how to test services in Angular 2 that have an Http dependency? Read more here!
Two-way Data Binding in Angular 2
Two-way data binding was one of the main selling points of Angular. In Angular 2, we can build directives that...
Resolving route data in Angular 2
We often want to make sure that certain data is available before a component is instantiated via routing. In this...
Angular 2 Animations - Foundation Concepts
Animation in Angular 2 is now easy and more intuitive... Learn foundational animation concepts and start animating your Angular 2...
Angular 2 is out - Get started here
Yes. The day has come. Angular 2 is finally released and here's how to get started.
Bypassing Providers in Angular 2
Dependencies are provided from the nearest ancestor provider in the injector tree. This article shows how to bypass it.
You might also be interested in
Exploring Angular 2 - Article SeriesRead more