Huupke Huupke - 2 years ago 167
Javascript Question

How to attach class method as jQuery EventHandler in TypeScript

I'm converting some JavaScript code to TypeScript and I can't figure out how the signature of an jQuery EventHandler should look like.

This is what I had in JavaScript but simplified to more generic terms (where I have some sort of pub-sub or observable pattern using custom events distributed via an element):

Observer.prototype._subscribe = function() {
this._row.bind('onItemChanged', this, this._onChangedHandler);
};
Observer.prototype._onChangedHandler= function(event, someString, someObject) {
var that = event.data;
if (someString === '42') {
that.coolMethod(someObject);
} else if (someString === '69') {
that.otherCoolMethod(someObject);
}
};


In another prototype I would call trigger to notify the observer with the event and at least 2 parameters of data (someString and someObject):

Subject.prototype.foo = function() {
// Trigger the event so Observer will be notified and pass in the string and the object (or any data whatsoever)
this._element.trigger("onItemChanged", ["42", this._data]);
};


Now I'm having a hard time to write this in TypeScript but this is what I thought it should lok like:

export class Observer {
private _subscribe (): void {
this._element.bind('onItemChanged', this, this._onChangedHandler);
}
private _onChangedHandler(event: JQueryEventObject, someString: string, someObject: FooBarClass) {
let that = event.data as Observer;
if (someString === '42') {
that.coolMethod(someObject);
} else if (someString === '69') {
that.otherCoolMethod(someObject);
}
}
}


This TypeScript doesn't compile but gives the following error:


Argument of type '(event: JQueryEventObject, someString: string, someObject: FooBarClass) => void' is not assignable to parameter of type 'EventHandler | EventHandlerBase>'.
Type '(event: JQueryEventObject, someString: string, someObject: FooBarClass) => void' is not assignable to type 'EventHandlerBase>'.
Types of parameters 'event' and 't' are incompatible.
Type 'Event' is not assignable to type 'JQueryEventObject'.


So how should the signature (parameter types) of the eventhandler look like?

P.S. If you like you can rewrite using jQuery::on() instead of jQuery::bind();

P.P.S. I'm not interested in the various ways how to get the proper 'this' unless I'm realy on the wrong path here.

Edit (after first replay):
As suggested I rewrote my TypeScript to make my problem more clear. I know of the fat arrow notation to get the right 'this' but like I previously said, I'm not interested in this. I want my method to be typesafe so, how should the signature of my handler method look like?
This indeed works:

private _subscribe (): void {
this._element.on('onItemChanged', () => this._onChangedHandler);
}
private _onChangedHandler(event: JQueryEventObject, someString: string, someObject: FooBarClass) {
}


But then I expected that this should work too:

private _subscribe(): void {
this._element.on('onItemChanged', (event: JQueryEventObject, someString: string, someObject: FooBarClass) => { this._onChangedHandler(event, someString, someObject); });
}


But I still can't get the correct Event type for the first parameter to get typesafety. With the 'any' type it does compile:

private _subscribe(): void {
this._element.on('onItemChanged', (event: any, someString: string, someObject: FooBarClass) => { this._onChangedHandler(event, someString, someObject); });
}

Answer Source

By putting the 'any' type and running in debugger I finally found out during runtime that the first parameter is of the 'JQuery.Event' type which also compiles correctly in TypeScript when using the type definitions of jQuery. Too bad tsc couldn't tell me this sooner at compile time (or I misinterpreted the error show in the question). So this is what it should look like (regardless of using bind, fat arrow or your own way of preserving the right context/this):

export class Observer {
  private _element: JQuery;

  private _subscribe(): void {
    this._element.on('onItemChanged', (event: JQuery.Event, someString: string, someObject: FooBarClass) => { this._onChangedHandler(event, someString, someObject); });
  }

  private _onChangedHandler(event: JQuery.Event, someString: string, someObject: FooBarClass): void {
  }
}

Although this compiles and gives some sort of typesafety, I'm a bit dissapointed since I can attach handlers with the wrong signature and it still compiles. I guess that is just how attaching eventhandlers work. At least you can call your methods directly with typesafety and still use them as handlers.

So for others running into this issue the signature should look like this:

export class Observer {
  private _element: JQuery;

  private _subscribe(): void {
    this._element.on('anyevent', () => this._yourOwnHandler);
  }

  private _yourOwnHandler(event: JQuery.Event, ...args: any[]): void {
  }
}

Where the '...args' can be anything you like that will give you the number and types of parameters you want.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download