I am a bit cofused by typescript decorators and how they are explained. Supposedly they 'decorate' a class by associating metadata with it. I can't really seem to grasp the concept of how that metadata is then associated with a class's instance. Take the angular 2 example.
@Component(
{
selector:...
}
)
export class foo { ... }
Currently as I understand angular will instantiate the class foo and somehow associate the instance with the decorator's arguments so that it can provide services, directives, and templates. All of this seems to be able to also be implementable via class inheritance. If I have a Component class and make my component extend that class why can't angular then provide those arguments when it bootstraps in the way that react does with props, and completely get rid of decorators in this use scenario.
class Component { ... } //say this has members such as selector, services, directives, etc..
class Foo extends Component { ... }
then you would instantiate it, at bootstrap/runtime with this
new Foo(ElementName, Directives, Services, etc..)
With react this is technically what went on under the hood. You derived component and implemented its methods. If you need to pass information when instantiating, then you pass in the props object.
please enlighten me.
This code surely looks similar:
import { Component } from '@angular/core';
export class AppComponent extends Component{ }
VS:
import { Component } from '@angular/core';
@Component()
export class AppComponent { }
You can do inheritance in JS in different ways - if you opt in for using the Class syntactic sugar and want to go with the extends
syntax for implementing inheritance then you are locked in with single inheritance
- so each class can only inherit one other class.
With @Decorators
it is possible to attach multiple behaviours to a class, thus creating multiple inheritance
not unlike with mixins. As Günter Zöchbauer pointed out, decorators are much easier to be evaluated statically, while inheritance has to be evaluated at runtime (i.e. if the compiler wants to know what kind of behaviours a specific child class has).
I look at @Decorators
as composition - as they are basically functions, I view them the same as higher-order functions, so it is like doing something like that, but more readable:
import { Component } from '@functionalComponent/core';
let myComponent = function() { return {hello: 'world'}}
let AppComponent = Component(myComponent())
Do not forget that Class
is in essence pure JS object, created/instantiated by a constructor function.
import { Component } from '@constructedComponent/core';
let MyComponent = function() {this.hello ='world'}
let AppComponent = new Component(new MyComponent)
Or go even 'lower' and imagine it as pure objects:
import { Component } from '@objectComponent/core';
let MyComponent = {this.hello ='world'}
let AppComponent = Component.augment(MyComponent)
Generally, the prototypal and functional natures of JS are very powerful and allow for mixing in behavior through calling functions and altering objects. That changes when you switch to the Class
syntax - at this point you should stick to OOP design principle known as Composition over inheritance and the decorators
are a neat way to express composition with a nice-to-read syntax:
Difference between the Composite Pattern and Decorator Pattern?
From Wikipedia:
Decorator use can be more efficient than subclassing, because an objects behavior can be augmented without instantiating an entirely new object.
As far as I know the main reason is to make it easy to evaluate statically. With inheritance you would need to execute TS (or transpiled JS) code to get the information.
Evaluating this metadata statically allows tools to use it for autocompletion and all kind of lint checks in the template as well as building designers and other tools that make it easier to build Angular applications.
Also the offline template compiler utilizes this metadata.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With