I cannot understand how 'this' context works in typescript. I cannot access class members in methods. Below is my code
class adopterDetailCtrl {
public adopter: IAdopter;
public $router: any;
static $inject = ['app.common.services.AdopterService'];
constructor(private adopterService: app.common.services.IAdopterServices) {
this.adopter = null;
}
$routerOnActivate(next) {
if (next.params.id > 0) {
this.getAdopterById(next.params.id);
}
}
getAdopterById(adopterId: number): void {
var AdopterList = this.adopterService.getAdopterById();
AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
});
}
setAdopter(data: IAdopter) {
this.adopter = data;//can access this.adopter
}
}
The this context is just the same in typescript as it as in javascript, as the code you actually run is the compiled javascript that the typescript compiler outputs.
In javascript you have two ways to deal with this issue:
Use the arrow function
Use the Function.prototype.bind function
You are probably passing getAdopterById as a callback, if that's the case then it will be easy to solve using bind:
let myobj = new adopterDetailCtrl(...);
...
someFunction(myobj.getAdopterById.bind(myobj));
You can also modify the reference for the method of the instance in the ctor:
(1)
class adopterDetailCtrl {
public adopter: IAdopter;
public $router: any;
static $inject = ['app.common.services.AdopterService'];
constructor(private adopterService: app.common.services.IAdopterServices) {
this.adopter = null;
this.getAdopterById = (adopterId: number) => {
var AdopterList = this.adopterService.getAdopterById();
AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
});
}
}
$routerOnActivate(next) {
if (next.params.id > 0) {
this.getAdopterById(next.params.id);
}
}
getAdopterById: (adopterId: number) => void;
setAdopter(data: IAdopter) {
this.adopter = data;//can access this.adopter
}
}
Notice that the method declaration is empty and the implementation is set in the ctor using the arrow function.
(2)
class adopterDetailCtrl {
public adopter: IAdopter;
public $router: any;
static $inject = ['app.common.services.AdopterService'];
constructor(private adopterService: app.common.services.IAdopterServices) {
this.adopter = null;
this.getAdopterById = this.getAdopterById.bind(this);
}
$routerOnActivate(next) {
if (next.params.id > 0) {
this.getAdopterById(next.params.id);
}
}
getAdopterById(adopterId: number): void {
var AdopterList = this.adopterService.getAdopterById();
AdopterList.query({ id: adopterId }, (data: adopter.IAdopter[]) => {
this.adopter = data[0];//this.adopter is undefined here. this refers to 'window'
});
}
setAdopter(data: IAdopter) {
this.adopter = data;//can access this.adopter
}
}
Here in the ctor you reassign the bound this.getAdopterById.bind(this) to this.getAdopterById.
In both of these cases you can freely pass the getAdopterById method as a callback and not worrying about the scope of this.
Another note on the arrow functions is that this is a new feature in ES6, and if you don't choose the ES6 target in your compilation options then the compiler won't actually use this notation but instead will convert this:
class A {
private x: number;
fn(): void {
setTimeout(() => console.log(this.x), 1);
}
}
To:
var A = (function () {
function A() {
}
A.prototype.fn = function () {
var _this = this;
setTimeout(function () { return console.log(_this.x); }, 1);
};
return A;
}());
This way the scope of this is saved in _this and in the callback function _this.x is used instead of this.x.
Related
I am trying to create a class which looks like this:
class Foo {
_bar?: () => void; // this is a property which will invoke a function, if it is defined
set bar(barFunctionDef: () => void) { // this stores a reference to the function which we'll want to invoke
this._bar = barFunctionDef;
}
get bar() { // this should either invoke the function, or return a reference to the function so that it can be invoked
return this._bar;
}
doSomething() { // this is what will be called in an external class
if(condition) {
this.bar; // this is where I would like to invoke the function (bar)
}
}
}
Essentially, I want to have a class which will store references to functions which will be optionally set. There will be many "bar" class properties which will all have the type () => void.
Is there a "correct" way to call this.bar inside doSomething in order to invoke the function that is stored on that property?
Don't know if that's what you're trying to achieve, but I set up an example of generic implementation here
type MethodReturnsVoid = () => void;
class Foo {
methods: Map<string, MethodReturnsVoid>;
constructor() {
this.methods = new Map();
}
public getMethod(methodName: string): MethodReturnsVoid {
if (this.methods.has(methodName)) {
return this.methods.get(methodName);
}
return null;
}
public setMethod(methodName: string, method: () => void): void {
this.methods.set(methodName, method);
}
}
const foo = new Foo();
const func: MethodReturnsVoid = () => console.log('func');
const anotherFunc: MethodReturnsVoid = () => console.log('anotherFunc');
foo.setMethod('func', func);
foo.setMethod('anotherFunc', anotherFunc);
const methodFunc = foo.getMethod('func');
if (methodFunc) methodFunc();
const methodAnotherFunc = foo.getMethod('anotherFunc');
if (methodAnotherFunc) methodAnotherFunc();
I've heard about mixin pattern yesterday, and I'm trying it but I'm confused with what I think is a basic concept.
Let's focus on this function :
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
On the base constructor prototype iterations, one of the property name is constructor. So what we do is reassign the derivedClass prototype constructor to the last baseClass prototype constructor.
Let's say we have three class :
class Disposable {
public isDisposed: boolean;
public dispose() {
this.isDisposed = true;
}
}
class Activable {
public isActive: boolean;
public activate() {
this.isActive = true;
}
public deactivate(){
this.isActive = false;
}
}
class MyDisposableObject implements Disposable, Activable {
//Disposable
public isDisposed = false;
public dispose: () => void;
//Activable
public isActive = false;
public activate:() => void;
public deactivate:() => void;
}
After applyMixins(MyDisposableObject, [Disposable, Activable]) we no longer have the right constructor on MyDisposableObject instances.
const m = new MyDisposableObject();
console.log(m.constructor); // output : f Activable() {}
console.log(m instanceof MyDisposableObject) //output : true !?
According to MDN documentation, The constructor property returns a reference to the Object constructor function that created the instance object. which is not the case anymore because applyMixins reassign the derivedClass.prototype.constructor. Can't this lead to side-effect ?
var Disposable = /** #class */ (function () {
function Disposable() {
}
Disposable.prototype.dispose = function () {
this.isDisposed = true;
};
return Disposable;
}());
var Activable = /** #class */ (function () {
function Activable() {
}
Activable.prototype.activate = function () {
this.isActive = true;
};
Activable.prototype.deactivate = function () {
this.isActive = false;
};
return Activable;
}());
var MyDisposableObject = /** #class */ (function () {
function MyDisposableObject() {
//Disposable
this.isDisposed = false;
//Activable
this.isActive = false;
}
return MyDisposableObject;
}());
applyMixins(MyDisposableObject, [Disposable, Activable]);
function applyMixins(derived, bases) {
bases.forEach(function (base) {
Object.getOwnPropertyNames(base.prototype).forEach(function (name) {
derived.prototype[name] = base.prototype[name];
});
});
}
var m = new MyDisposableObject();
console.log('m constructor :', m.constructor);
console.log('m instanceof MyDisposableObject : ', m instanceof MyDisposableObject);
EDIT :
I still don't understand why the documentation example reassign the constructor of derivedClass.prototype and if this is harmful or not so while I wait for a great explanation, I modified the applyMixins to ignore constructor property :
function applyMixins(derived, bases) {
bases.forEach((base) => {
Object.getOwnPropertyNames(base.prototype)
.filter((name) => name.toLowerCase() !== 'constructor')
.forEach((name) => {
derived.prototype[name] = base.prototype[name];
})
})
}
How to dynamically extend class with method in TypeScript? Any examples?
I try:
UsersBlocksMyOrders.prototype.myFunc = function():any{
alert('23434');
};
But compiler give me a error.
Most often, you need to do something like:
interface UsersBlocksMyOrders {
myFunc(): any;
}
Otherwise the compiler doesn't know about it.
It even works with existing classes. For example:
interface String {
logit(): void;
}
String.prototype.logit = function () {
console.log(this);
}
let a = "string";
a.logit();
(code in playground)
Because you want to change something in a different module, which is called Module Augmentation, you need to do something like:
Import { UsersBlocksMyOrders } from "../pages/users/blocks/myorders";
declare module "../pages/users/blocks/myorders" {
interface UsersBlocksMyOrders {
logit(): void;
}
}
UsersBlocksMyOrders.prototype.logit = function () { console.log(this); }
Whenever possible (which it seems to be for you), edit the source code directly. Doing it like this should only be done on an as-needed basis.
This may help (from here):
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
I have an inherited class, and need the parent to have a virtual method, which is overridden in the child class. This method is called from the base constructor, and needs access to instance properties, so it needs to be a lambda function, so "this" is "_this". The problem is, overriding a lambda method does not work for me like overriding a non-lambda does. Is this possible? If not, I'd like to understand why.
Also, will "this" always be the same as "_this" when the method is only called from the constructor?
class Base {
protected prop = null;
constructor() {
this.init();
this.initLambda();
}
init() {
console.log("Base init");
}
initLambda = () => {
console.log("Base initLambda");
}
}
class Derived extends Base {
constructor() {
super();
}
init() {
console.log("Derived init");
}
initLambda = () => {
//let x = this.prop;
console.log("Derived initLambda");
}
}
Output:
Derived init
Base initLambda
Well, you can't have that.
There's an issue that was opened but it was closed as "by design".
You should use regular methods:
class Base {
protected prop = null;
constructor() {
this.init();
this.initLambda();
}
init() {
console.log("Base init");
}
initLambda() {
console.log("Base initLambda");
}
}
class Derived extends Base {
constructor() {
super();
}
init() {
console.log("Derived init");
}
initLambda() {
console.log("Derived initLambda");
}
}
And then it will work.
As for keeping the right this, you can always pass a call to the method as an arrow function:
doit() {
setTimeout(() => this.init(), 1);
}
Or use the Function.prototype.bind function:
setTimeout(this.init.bind(this));
Also, the _this thing that the typescript compiler produces is just a hack to polyfil the arrow functions for ES5, but if you change the target to ES6 then it won't use it.
Edit:
You can save the bound methods as members:
class Base {
...
public boundInit: () => void;
constructor() {
...
this.boundInit = this.initLambda.bind(this);
setTimeout(this.boundInit, 500);
}
...
With that, when I do new Derived() this is what I get:
Derived init
Derived initLambda // after 200 millis
The problem is that your lambda is a property.
When compiled to javascript, the Baseclass becomes
var Base = (function () {
function Base() {
this.prop = null;
this.initLambda = function () {
console.log("Base initLambda");
};
this.init();
this.initLambda();
}
Base.prototype.init = function () {
console.log("Base init");
};
return Base;
}());
As you can see initLambda is defined inside the constructor of Base, so there is no way you can override that.
Calling super() calls the Base constructor which defines the this.initLambda with the code in Base and runs it. Hence your result.
View on playground
Try using using a getter property.
i.E.
get initLambda() {
return () => {
console.log("...");
}
}
In such way that the whole code looks like that:
class Base {
protected prop = null;
constructor() {
this.init();
this.initLambda();
}
init() {
console.log("Base init");
}
get initLambda() {
return () => {
console.log("Base initLambda");
}
}
}
class Derived extends Base {
constructor() {
super();
}
init() {
console.log("Derived init");
}
get initLambda() {
return () => {
//let x = this.prop;
console.log("Derived initLambda");
}
}
}
I have just started out with Typescript and want to convert the following Javascript into Typescript.
The first code block is the actual JS and the second is the TypeScript which I have so far.
I think it's mostly right but the bit I am having trouble working out is the return function at the end of the Javascript. How would I write this bit in my TypeScript code?
var ABC = ABC || {};
ABC.navigationService = function () {
var navigate = {};
function navigateChart(e, query) {
}
function drillChart(direction, type, query) {
}
function canDrillUp(series, query) {
}
function drillUp(series, query) {
}
return {
navigate: navigateChart,
drill: drillChart,
canDrillUp: canDrillUp,
drillUp: drillUp
}
}
angular.module("MyApp").service("navigationService", [ABC.navigationService]);
and with the typescript, this is what I have so far, including only the functions for which I want to return the results as in the Javascript:-
module ABC.Visualisation.Services {
'use strict';
var xPos = 0;
var yPos = 0;
var canDrilldownPoint = false;
var canDrillupPoint = false;
var canDrilldownSeries = false;
var canDrillupSeries = false;
var navigate = {
loading: false,
showGui: false,
canDrilldownPoint: canDrilldownPoint,
canDrillupPoint: canDrillupPoint,
canDrilldownSeries: canDrilldownSeries,
canDrillupSeries: canDrillupSeries,
x: xPos,
y: yPos
};
export class NavigationService implements INavigationService {
public navigateChart(e: any, query: any) {
}
public drillChart(direction: string, type: string, query: any): void {
}
public canDrillUp(series: any, query: any): boolean {
}
public drillUp(series: any, query: any): void {
}
}
}
angular.module("MyApp").service("NavigationService", [ABC.Visualisation.Services.NavigationService]);
1) I believe that
angular.module("MyApp").service("NavigationService", [ABC.Visualisation.Services.NavigationService]);
should be
angular.module("MyApp").service("NavigationService", ABC.Visualisation.Services.NavigationService);
2) This is how you can put variables to a module: Creating a variable with get/set inside a module in Typescript