Typescript method of the overridden class and this

I have two Typescript classes:

class Base { value: string; lambdaExample = () => { this.value = 'one'; } methodExample() { this.value = 'two'; } } class Child extends Base { lambdaExample = () => { super.lambdaExample(); // Error, because I've overwritten (instead of overridden) the method this.value = 'three'; // ok } methodExample() => { super.methodExample(); // ok this.value = 'four'; // Error: this refers to window, not to the actual this } } 

How can I write my methods so that this references are reliable, and I can override methods and call them from the parent class?

+5
source share
4 answers

Actually a good look at the various ways to solve this problem on the Microsoft Git Wiki . Essentially, it boils down to the following:

  • Bind or wrap a method each time it is called from a different context if you care about inheritance.
  • Turn the method into a property that contains the function, if you do not.

There are many more points on a real wiki, and I really recommend that you read all of this.

EDIT

Wrapping example:

Given the class

 class SomeClass { public someProp: string = "Hello World"; public someMethod() { console.log(this.someProp); } } 

If you were to call someMethod from a (for example) click handler - someEl.onclick = instanceOfMyClass.someMethod; - an exception would be thrown (it is assumed that window does not have someProp property).

You can get around this by binding the function to instanceOfMyClass (not supporting a security type, not compatible with ES6) or manually wrapping it (in fact, what the binding does):

 someEl.onclick = function() { someInstanceOfMyClass.someMethod(); }; 

It's a bit verbose and pedantic, but by invoking someMethod as the someInstanceOfMyClass property and not passing it to the event handler (which turns it into a window property), you guarantee that this always an instance of MyClass .

+2
source

This coverage problem can be solved with a very simple class decorator, and you no longer need to use the ugly * syntax of the arrow function for methods - or think about the problems again:

 function BindMethods(target: any): any { var originalCtor = target; var newCtor: any = function (...args) { var c: any = function () { // Methods are defined on prototype. var prototype = Object.getPrototypeOf(this); // Bind methods. Object.keys(prototype).forEach(propertyKey => { if (typeof this[propertyKey] === "function") { prototype[propertyKey] = this[propertyKey].bind(this); } }); // Invoke original constructor. return originalCtor.apply(this, args); } c.prototype = originalCtor.prototype; return new c(); } // Copy prototype so 'intanceof' still works. newCtor.prototype = originalCtor.prototype; // Overrides original constructor. return newCtor; } 

Using it is as simple as binding it to a class ( methodExample been changed for demo purposes only):

 @BindMethods class Base { value: string; methodExample() { console.log(this.value); } } 

This ensures that the reference to this always correct (even with inheritance):

 var base = new Base(); base.value = "two"; base.methodExample(); // "two" setTimeout(base.methodExample, 20); // "two" 

Unfortunately, there is no way to bind methods one by one using method decorators, because an instance reference is needed. If this is a requirement, you can use decorator factory to pass the property keys of the methods to bind.


* ugly when used for methods.

+2
source

Your assumptions about the causes of errors are incorrect, and I think this is the cause of your problem ... at least as far as I understand.

 lambdaExample = () => { this.value = 'one'; } 

This line, for example, defines a property, not a method on Base , and you cannot override a property. The only instance method you defined in Base is methodExample .

In Child you assign a new lambaExample variable. Your call to super.lambaExample() fails because access to them is only possible with super() ; access to properties is through this . methodExample in your Child class appears as a syntax error for me.

Note that you can still call super from Child in the rewritten lambaExample property, but only in methods. It works:

 lambdaExample = () => { super.methodExample(); // Success on super.<somemethod>() this.value = 'three'; } 

I know only one way to declare an instance method in a class, and if you agree with this syntax, this works the way you expected:

 class Base { value: string; lambdaExample() { this.value = 'one'; } methodExample() { this.value = 'two'; } } class Child extends Base { lambdaExample() { super.lambdaExample(); this.value = 'three'; } methodExample() { super.methodExample(); this.value = 'four'; } } 
+1
source

I found a workaround, but this is ugly:

 class Base { someFunction = () => { this.otherFunction(); } protected otherFunction = () => { // actual implementation here } } class Child { someFunction = () => { this.otherFunction(); // additional implementation here } } 

That way, you can call someFunction on any instance and still access the original implementation using otherFunction .

+1
source

All Articles