Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript event handler function for event type field - Incorrect context

This is a jquery interface from jquery.d.ts:

export interface IDialogEvent extends DialogEvent {
    (event: Event, ui: DialogUIParams): void;
}

This is my custom interface mimicking partial functionality of the DialogOptions interface of jquery.d.ts:

export interface IDialogOptions {
    open: IDialogEvent;
}

export class DialogClass implements IDialogOptions { 

    //Dialog options
    public open: IDialogEvent;

    //Class related fields
    public someField: any;
    public dialogEl: JQuery;

    constructor() { 
        this.open = this.OpenHandler;
        this.dialogEl = $("<div></div>").dialog(this); 
        //Passing "this" initializes the dialog by mapping relevant class fields
        //to the dialog's "option" object, in this case the only "relevant" field is "open".
    }

    public OpenHandler(event: Event, ui: DialogUIParams) { 
        var value = this.someField; //BAD. "this" is not type BaseClass
    }

    public NonEventHandlerMethod() { 
        var value = this.someField; //GOOD. "this" is type BaseClass
    }  
}

var dialog = new DialogClass();
dialog.dialogEl.dialog("open"); 

The last line fires OpenHandler() but inside it, this is not type BaseDialog (unlike in NonEventHandlerMethod).

The reason I need an event handler function for the dialog options field and the reason why I can't simply do this:

 export class DialogClass implements IDialogOptions { 
     ...
      constructor() { 
          this.open = () => {
                //event handling logic
          };
          ...
      }
      ...
 }        

is because I need to add additional open-event handling logic in classes that extend DialogClass and there is no differentiation between this.member and super.member... there is only differentiation between this.function() and super.function():

 export class LoginDialog extends DialogClass { 
     ...
      constructor() { 
          this.open = this.OpenHandler;
          ...
      }

      public OpenHandler(event: Event, ui: DialogUIParams) { 
           super.OpenHandler(); //Base handling logic

           //Additional handling logic
      } 
      ...
 } 

I think this may be a bug because

   export class DialogClass implements IDialogOptions { 
     ...
      constructor() { 
          this.open = () => {
                var test = this.someField;  //Correct context
          };
          ...
      }
      ...
   }  

and calling the method directly:

   var dialog = new DialogClass();
   dialog.OpenHandler();  //Correct context when called directly
   //Note: I haven't actually tested this persay but this function is no different
   //than any other functionso a direct call should certainly not be problem.
like image 308
parliament Avatar asked Jan 23 '13 18:01

parliament


1 Answers

TypeScript follows the usual JavaScript scoping conventions, so this will be dependent on context. If you have a method on a class that fires based on an event, this will be the event target. When you directly call a method on a class, this will be the class.

If you want to get around this, you can take advantage of how JavaScript walks up the scope chain by giving this an alias...

Here is one way to do that:

this.open = () => { this.OpenHandler(this); };

The arrow-function syntax creates and alias name _this in the JavaScript.

public OpenHandler(context: DialogClass, event: Event, ui: DialogUIParams) { 
    var value = context.someField;
}

We accept the cleverly aliased version of this as a parameter and context.someField should have the value we are after.

like image 110
Fenton Avatar answered Oct 22 '22 12:10

Fenton