Reputation: 725
I'm migrating a angular 1 application from javascript to typescript. Actually everything is done but I can't get my head arround one specific case.
I have some directives that use a link function.
in pseudocode:
class SomeDirective implements ng.IDirective{
static $inject=['someService'];
public restrict: string='EA';
public scope: any;
constructor(private someService: SomeService){
this.scope={
data: '='
}
}
}
now I want to Define a link function like
public link(scope, ele, attrs){
do some action with this.someService.someField
}
If I define this method in the class then I get an error: property 'someField' not found on undefined.
If I define the method as follows in the constructor then it does work. What is the difference?
this.link=(scope,ele,attrs)=>{
do some action with this.someService.someField
}
link is defined here as a class property of type any.
Upvotes: 0
Views: 397
Reputation: 164467
Defining the function in the constructor creates, as you wrote, a class property, and it's assigned to the instance only.
A class method on the other hand is defined on the prototype:
class A {
fn2: () => void;
constructor() {
this.fn2 = () => {
console.log("A.fn2");
};
}
fn1(): void {
console.log("A.fn1");
}
}
Compiles into:
var A = (function () {
function A() {
this.fn2 = function () {
console.log("A.fn2");
};
}
A.prototype.fn1 = function () {
console.log("A.fn1");
};
return A;
}());
Using the instance function is problematic if you want to override the function in a derived class:
class B extends A {
constructor() {
super();
this.fn2 = () => {
super.fn2(); // Error: Only public and protected methods of the base class are accessible via the 'super' keyword
console.log("B.fn2");
};
}
fn1(): void {
super.fn1();
console.log("A.fn1");
}
}
You can save a reference for the previous this.fn2
before assigning the new function (in B
) so that you'll have a super to call, but that isn't very comfortable.
The plus of course (if you're using arrow functions) is that the functions are bound to the right this
out of the box.
It's up to you to decide what's more important to you, I prefer the methods way, and if I pass them as callbacks then I just bind them.
The right this
should always be correct when you invoke the methods regularly:
class A {
bound: () => void;
constructor() {
this.bound = () => {
console.log("bound: ", this);
};
}
unbound(): void {
console.log("unbound: ", this);
}
}
let a1 = new A();
a1.bound(); // A {}
a1.unbound(); // A {}
As expected, this
here is the instance of A
, but:
function later(fn: Function) {
setTimeout(fn, 100);
}
later(a1.bound); // A {}
later(a1.unbound); // Window { ... }
As you can see here, when invoking a1.unbound
using a setTimeout
(or if you pass it as a callback) then this
isn't the instance of A
.
That can be fixed like so:
later(a1.unbound.bind(a1)); // A {}
Upvotes: 1