Referenced:
Override constructor with an class decorator?
http://blog.wolksoftware.com/decorators-metadata-reflection-in-typescript-from-novice-to-expert-part-ii
I'd like to wrap a classes constructor with a custom one when applying a method decorator (ie in the same way you could do with a class decorator). I require this as I have another component that will call into the class, and will execute the method that was decorated. Its not hard to have the component execute the method that was decorated, however because the decorator is run before the class is instantiated the decorated method will not be associated with a class instance and therefore will not have access to any of the classes state (ie this == undefined). So I want to be able to provide the method reference to the component during class instantiation (ie constructor) so that it is bound to the current instance.
So I'd like to do this (typescript):
class Foo {
constructor(private value) { }
#execute
bar() {
return this.value;
}
}
Which would have the same affect as doing this:
class Foo {
constructor(private value) {
StaticComponent.AddReference(this.bar().bind(this));
}
bar() {
return this.value;
}
}
If my other component has a reference to bar() it should be able to execute it with bar having full access to its instance.
I've tried to override the prototype of the target etc in a similar way you might do so for a class decorator, but couldn't get that to work.
Related
I am learning how to use decorators in Javascript and am confused as to when its appropriate to use class inheritance vs decorators?
For instance the following uses decorators to add a method to a class. It also overwrites one of the decorated classes methods.
import $ from "jquery";
function write(msg) {
$("#app").prepend(msg);
}
function clean(constructor) {
return class extends constructor {
foo() {
write("<h1>from the Decorated class</h1>");
}
bar() {
write("I haz bar");
}
};
}
#clean
class Hello {
foo() {
write("<h1>from the undecorated class</h1>");
}
}
const h = new Hello();
h.();
AFAIK, with inheritance — if the parent and child classes have the same methods, it would be flipped: child would overwrite the parent. In a situation where I would NOT want this to happen, decorators would be the desired choice. Is that correct?
P.S.
I know decorators not officially in the language yet.
Is there any reason to write classic syntax of ES6 methods?
class MyClass {
myMethod() {
this.myVariable++;
}
}
When I use myMethod() as callback on some event, I must write something like this (in JSX):
// Anonymous function.
onClick={() => { this.myMethod(); }}
// Or bind this.
onClick={this.myMethod.bind(this)}
But if I declare method as arrow function:
class MyClass {
myMethod = () => {
this.myVariable++;
}
}
than I can write just (in JSX):
onClick={this.myMethod}
The feature you are using is not part of ES6. It's the class fields proposal. It allows you to initialize instance properties without having to write a constructor. I.e. your code:
class MyClass {
myMethod = () => {
this.myVariable++;
}
}
is exactly the same as
class MyClass {
constructor() {
this.myMethod = () => {
this.myVariable++;
};
}
}
And this also shows you what the difference is between a normal class method an a method created via a class field:
A normal method is shared between all instances of the class (it is defined on the prototype)
A "class field method" is created per instance
So all the same as reasons as presented in Use of 'prototype' vs. 'this' in JavaScript? apply, but in short:
Use "class field methods" if you need a method per instance. Such is the case for event handlers that need to access the current instance. Access to this also only works if you are using an arrow function.
Use normal class methods in all other cases.
How can I execute a function that is aware of a newly created class's name which is derived from a certain parent at the time that the class is extended?
What I currently do:
class component {
constructor() {
// this.constructor.name returns the derived class name on instantiation
// we need to register class components derived from component
registerIfUnknown(this.constructor.name);
}
}
However this means the registerIfUnknown function will be called every time we create a new instance of any subclasses of component. Is there a way to execute a method/function that understands what the class's name is whenever the component class is extended?
I'm decorating a class to provide arguments to it's constructor, the problem arises when I try to subclass this class:
#decorate('foo', 'bar')
class Foo {
constructor(foo, bar) {}
}
class Bar extends Foo {}
function decorate(foo, bar) {
return function(ctor) {
return ctor.bind(null, foo, bar);
};
}
The above won't work because of the null context passed to the constructor (at least I think that is the root of the issue). When used with Babel, this is the following error: " Object prototype may only be an Object or null: undefined"
Is there any way to both decorate the parent class and extend the child?
Trying to extend a function that has been created via .bind is not going to work for you. Bound functions have tons of special logic around them. Bound functions do not have a .prototype property, which means extending a bound function would mean there was no prototype chain to follow. The proper way to implement this decorator would be to do
function decorate(foo, bar) {
return ctor => class extends ctor {
constructor(...args){
super(foo, bar, ...args);
}
};
}
While attempting to apply a decorator to a class' method, It's seemingly applying it to the class instead. Me, being not all that familiar with decorators/annotations, It's more than likely user-error.
Here's a really quick example that I've whipped up:
class Decorators {
static x (y, z) {
return method => {
// do stuff with y, z, method
// method should be the method that I decorate
}
}
}
class Foo {
#Decorators.x('foo', 'bar')
static main () {
// ...
}
}
As you can see, inside of the decorator, the method should be equal to the static main method, but when I add a console.log to the decorator to see what the method is, it logs [Function: Foo] (which is the Foo class after transpilation via Babel)...
From what I can tell, Babel.js is applying the decorator to the class, even though it's set on the method. I could be wrong, though.
The first parameter is the target (it can be the class constructor - for statics, the prototype - for non-statics, and the instance, in case of properties). And the second one is the method's name:
return (target, name, descriptor) => {
console.log(name);
}