"Exploring the Power of TypeScript Types with the .bind Method

Delving into the world of generics, I've crafted a generic event class that looks something like this:

export interface Listener < T > {
  (event: T): any;
}

export class EventTyped < T > {
  //Array of listeners
  private listeners: Listener < T > [] = [];

  Attach(listener: Listener < T > ) {
    this.listeners.push(listener);
  }

  Emit(event: T) {
    this.listeners.forEach(listener => listener(event));
  }
}

Creating an event instance is as simple as

onPageSizeSelected = new EventType<PageSizeSelector>();

The signature for my listeners is defined as

PageSizeSelectedHandler(event:Event,object:PageSizeSelector)
.

When trying to attach the event handler using

pageSizeSelector.onPageSizeSelected.Attach(this.PageSizeSelectedHandler.bind(this))
, no error is thrown. However, if I attach the handler without using `bind` like this
pageSizeSelector.onPageSizeSelected.Attach(this.PageSizeSelectedHandler)
, TypeScript immediately detects that the method signature is incorrect and indicates too many parameters.

What exactly is `bind` doing that TypeScript struggles to infer the method signature correctly? How can I ensure my event remains strongly typed while preserving the context of `this` safely?

Answer №1

For those interested in ensuring the compiler identifies bound methods with incorrect parameter counts without focusing on the this context, consider enabling the --strictBindCallApply compiler option:

class StringListeningClassThing {
  myString = "hey";
  oneParam(x: string) {
    return x + this.myString;
  }
  twoParams(x: number, y: string) {
    return x.toFixed(2) + y + this.myString;
  }
}

const onPageSizeSelected = new EventTyped<string>();
const stringListenerThingy = new StringListeningClassThing();

onPageSizeSelected.Attach(
  stringListenerThingy.twoParams); // error
onPageSizeSelected.Attach(
  stringListenerThingy.twoParams.bind(stringListenerThingy)); // error
onPageSizeSelected.Attach(
  stringListenerThingy.oneParam.bind(stringListenerThingy)); // okay
onPageSizeSelected.Attach(
  stringListenerThingy.twoParams.bind(stringListenerThingy, 2)); // okay

The above steps may serve your needs. However, there are still some type safety concerns at play:


Regrettably, TypeScript does not naturally excel at type-checking this contexts:

onPageSizeSelected.Attach(
  stringListenerThingy.oneParam); // no error 
onPageSizeSelected.Attach(
  stringListenerThingy.oneParam.bind({ notGood: true })); // no error

Accepting these scenarios could lead to runtime errors as stringListenerThingy's methods may dereference an incorrect this.

There is an ongoing discussion at microsoft/TypeScript#7968 regarding the addition of a --strictThis compiler option to prevent the propagation of mis-bound functions. While this proposal has not been implemented yet, it has been acknowledged that adoption could have significant impacts due to potential breaks in existing code and compiler performance. Users who advocate for this feature should engage on the issue and share their perspectives.

If enforcing this check is imperative, users have the option to manually introduce this parameters throughout their codebase, as demonstrated below:

// Explicitly define void this-context for Listener interface
export interface Listener<T> {
  (this: void, event: T): any;
}

// Explicitly assign class-based this-context to all methods 
class StringListeningClassThing {
  myString = "hey";
  oneParam(this: StringListeningClassThing, x: string) {
    return x + this.myString;
  }
  twoParams(this: StringListeningClassThing, x: number, y: string) {
    return x.toFixed(2) + y + this.myString;
  }
}

Following these adjustments, errors highlighted in the previous examples will now be flagged:

// Achieve type safety   
onPageSizeSelected.Attach(
  stringListenerThingy.oneParam); // error 
onPageSizeSelected.Attach(
  stringListenerThingy.oneParam.bind({ notGood: true })); // error

Therefore, while the compiler can indeed enforce these rules, it currently requires manual intervention until the potential implementation of --strictThis.

Explore the code on TypeScript Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to dynamically manipulate HTML elements in Angular 5

Recently, I've been attempting to programmatically transform an HTML element. Strangely, when I update the transform value in the console tab, it changes successfully, but for some reason it doesn't reflect in the element tab of the browser. onD ...

Design interactive Vue form with customized questions based on user response

I am looking to dynamically create a form with conditional fields. The structure of the form is stored in an object called Q. Below is an example of a Vue component that utilizes bootstrap-vue. <template> <div> <div v-for="q of ...

Ensure page is updated after an AJAX request in jQuery Mobile by refreshing the page

My jQueryMobile page structure in index.html looks like this: <div data-role="page"> <div data-role="header">...</div> <div data-role="content">...</div> <div data-role="footer">...</div> </div& ...

Luxon DateTime TS Error: The 'DateTime' namespace cannot be used as a type in this context

I have encountered an issue while trying to set the type of a luxon 'DateTime' object in TypeScript. The error message DateTime: Cannot use namespace 'DateTime' as a type appears every time I attempt to assign DateTime as a type. Below ...

JavaScript double-click functionality is not operational on IE9 and IE11 browsers

My JavaScript code has an issue where single-click opens a link in a new tab and double-click opens a lightbox. This works perfectly in all browsers except for IE9 and IE11. In my initial code, both single-click and double-click function correctly. However ...

Using MDBootstrap for reactjs, incorporating a modal into a table for enhanced functionality

As a newcomer to the world of React.js and Material Design Bootstrap, I am attempting to load a dataset onto a table using a mock JSON file. After some trial and error, I managed to achieve a certain level of success with this task. My goal is to include a ...

Navigating within an entity

Currently working on coding with javascript & react. There is a specific object called "rates" that I am attempting to iterate through: { "M": { "lab": { "S": "50%" }, "dme": { "S": "75%" }, ...

I noticed that my regular expression is functioning correctly on regex101, but for some reason, it's

My search works perfectly on regex101 using this link: https://regex101.com/r/z8JCpv/1 However, in my Node script, the third matched group array[2] is returning not only the matching text but also everything following it. An example line from the source ...

Guide to transforming Excel formulas into JavaScript

In my Excel sheet, I have the following formula: =IF($F6=0,"",IF(I6=0,"",$F6/I6)) where F6=7000 and I6 is empty. The Excel result is displaying no data for the formula. Now, I need to convert this formula into JavaScript. function AB6(F6) { var ...

javascript - developing a neutral constructor

Similar Question: Constructors in Javascript objects I am exploring the concept of creating classes in JavaScript. I am struggling to grasp it fully. Now, I am curious if it's possible to create a constructor in JavaScript, similar to what can b ...

In Angular, additional code blocks are executed following the subscription

I am facing an issue with my file upload function. After the file is uploaded, it returns the uploaded path which I then pass to a TinyURL function this.tinyUrl.shorten(data.url).subscribe(sUrl => { shortUrl=sUrl;});. However, there is a delay in receiv ...

Utilizing Angular 2 for Element Selection and Event Handling

function onLoaded() { var firstColumnBody = document.querySelector(".fix-column > .tbody"), restColumnsBody = document.querySelector(".rest-columns > .tbody"), restColumnsHead = document.querySelector(".rest-columns > .thead"); res ...

In React, I am currently working on implementing a feature that will allow the scene camera to move as the user scrolls

I'm currently working on incorporating a feature to enable movement of the scene camera when scrolling in React. However, as I am relatively new to using this library, I am unsure which implementation approach would be most suitable for achieving this ...

Difficulty in transferring JavaScript variable to PHP

After encountering a basic issue, I learned that PHP runs server-side and executes on pageload even if the include is nestled in an AJAX callback. Initially, I could display query results by returning PHP in the JavaScript value attribute, but failing to i ...

Having difficulty utilizing defineProps in TypeScript

For some time now, I've been utilizing withDefaults and defineProps. Unfortunately, this setup has recently started failing, leaving me puzzled as to why! Here's a simple SFC example: <script setup lang = "ts"> const props ...

Efficiently Measuring the Visibility Area of DetailsList in Fluent UI

I am currently utilizing the DetalisList component alongside the ScrollablePane to ensure that the header remains fixed while allowing the rows to scroll. However, I have encountered a challenge as I need to manually specify the height of the scrollable co ...

Tips on creating animations for elements that are triggered when scrolling and become visible

Is there a way to animate an element when it becomes visible on scroll, rather than at a fixed position on the page? I'm currently using code that triggers the animation based on an absolute scroll position, but I'm looking for a more dynamic sol ...

Angular unable to send object to HTML page

Struggling with learning angular, encountering new challenges when working with objects in different components. In two separate instances, try to implement two different mechanisms (microservice or service component serving an object directly). This speci ...

Toggle visibility of various items in a to-do list, displaying only one item at a time with the use of JavaScript

I am currently working on a web project using the Laravel framework. I am struggling with implementing a feature where only the title of each to-do item is displayed, and when clicked, it should reveal the corresponding content. However, I have encountered ...

Why are the HTML links generated by JS not opening in Chrome?

<a href='http://www.xyz.hu/xyz' alt='Kosár' title='Kosár'>Megtekintés</a> Additionally: - A setInterval function refreshes the sibling's content every second, although it should not affect this specific el ...