It appears that TypeScript is generating incorrect 'this' code without giving any warning

I seem to be facing some resistance filing a feature request related to this on GitHub issues, so I'll give it a shot here.

Here is the code snippet that caused me trouble:

export class Example {
    readonly myOtherElement: HTMLElement;

    public constructor() {
        this.myOtherElement = <HTMLFormElement>document.getElementById('myFormElement');

        document.querySelector("form").addEventListener('submit', this.sendMessage);
    }

    private sendMessage(e: Event) {
        this.myOtherElement.style.display = 'none';
    }
}

This code needs to be adjusted for ES2015 output as follows:

            Example = class Example {
                constructor() {
                    this.myOtherElement = document.getElementById('myFormElement');
                    document.querySelector("form").addEventListener('submit', this.sendMessage);
                }
                sendMessage(e) {
                    this.myOtherElement.style.display = 'none';
                }
            };

The problem lies in this code. The context of this changes within the event handler and causes this.myOtherElement to become undefined.

To fix this issue, a lambda can be used:

document.querySelector("form").addEventListener('submit', e => this.sendMessage(e));

However, TypeScript IntelliSense inaccurately assumes that this.myOtherElement will point to an HTMLElement, leading to potential runtime errors. Adjusting TypeScript accordingly results in compilation errors:

    private sendMessage(e: Event) {
        this.style.display = 'none';
    }

TypeScript still thinks this is an instance of Example, resulting in the following error:

Error TS2339 (TS) Property 'style' does not exist on type 'Example'.

In my opinion, one of the following solutions should be implemented:

  • When using

    addEventListener('submit', this.sendMessage)
    , the compiler should warn if there are references to this in sendMessage.

  • The emitted code should differ based on context.

It's unclear whether the TypeScript team acknowledges this issue or if it's a challenging problem to solve?

Answer №1

It is important to note that the emitted code in this scenario is functioning correctly. The use of this within a function can vary depending on who is calling it. To avoid any confusion, you have the option to bind a function to a specific this context.

When defining a class member function, TypeScript lacks the ability to anticipate how this will change as it is subject to runtime modifications.

In the case of your sendMessage member, this could potentially refer to various contexts such as the class itself or the caller's context.

An example would be if another member function called sendMessage from the class context.

Due to the unpredictability of what context this will point to, TypeScript cannot definitively associate it with the Example class context.

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

Issue with React useCallback not being triggered upon a change in its dependencies

useCallback seems to be capturing the wrong value of its dependency each time. const [state, setState] = React.useState(0); const callback = React.useCallback(() => { console.log(state); // always prints 0, why? }, [state]); React.useEffec ...

Leveraging interfaces with the logical OR operator

Imagine a scenario where we have a slider component with an Input that can accept either Products or Teasers. public productsWithTeasers: (Product | Teaser)[]; When attempting to iterate through this array, an error is thrown in VS Code. <div *ngFor= ...

Displaying a child component as a standalone page rather than integrating it within the parent component's body

I'm currently working on implementing nested navigation in my website, but I am facing challenges with loading the child component without the need to include a router-outlet in the parent component. This setup is causing the child component's co ...

Error message: "The use of Vue 3 refs in the render function is

I am facing an issue with my Vue component wherein the root element is set as ref="divRef". Strangely, when I try to access divRef.value inside the onMounted function, it returns undefined. Any assistance on this matter would be greatly appreci ...

Try querying again if you receive no results from an http.get request in Angular using RXJS Operators

In my Angular service, I sometimes encounter an issue where I receive an empty array. In such cases, I would like to trigger a fresh query. let request = this.http.post(this.searchlUrl, payload).pipe( retryWhen(errors => errors.pipe(delay(100 ...

Intercept Axios Responses - Retrieving API Responses for HTTP Statuses that are not in the 200 range

I've set up a custom Axios instance with interceptors for handling responses. As per the Axios documentation, the success interceptor is triggered for 2xx statuses while the error interceptor handles any other status codes. My goal is to show an error ...

Generate a basic collection of strings from an object

Looking at this object structure Names = [ { group: 'BII', categories: null }, { group: 'GVL', categories: [] } ]; I ...

An issue occurred in React 16.14.0 where the error "ReferenceError: exports is not defined" was not properly

As the creator of the next-translate library, I have encountered a perplexing issue with an experimental version involving an error specifically related to React 16.14.0. Interestingly, upgrading React to version 17 resolves the issue, but I am hesitant to ...

Unit testing in Typescript often involves the practice of mocking

One challenge with mocking in Typescript arises when dealing with complex objects, as is the case with any strongly-typed language. Sometimes additional elements need to be mocked just to ensure code compilation, such as using AutoFixture in C#. In contras ...

Adding typing to Firebase Functions handlers V2: A step-by-step guide

Here's a function I am currently working with: export async function onDeleteVideo(event: FirestoreEvent<QueryDocumentSnapshot, { uid: string }>): Promise<any> { if (!event) { return } const { disposables } = event.data.data() ...

Error: Undefined object trying to access 'vibrate' property

Good day, I apologize for my poor English. I am encountering an issue with Ionic Capacitor while attempting to utilize the Vibration plugin. The documentation lacks detailed information, and when checking the Android Studio terminal, I found the following ...

Whenever the return condition is false, make sure to subscribe to the Angular CanActivate Guard

In my UserAccessGuard class, I have a method that captures the current path and compares it to the user's available paths. However, I am facing asynchronous problems because the condition inside the subscribe block causes my Hasaccess variable to rema ...

Retrieve a single user using a query in the GraphQL platform

I've been struggling to create a GraphQL query in Express that retrieves only one user instead of all users every time. This is the query I'm using, incorporating TypeORM as my ORM: import { GraphQLList, GraphQLID } from 'graphql'; imp ...

What is the trick to maintaining the chiplist autocomplete suggestions visible even after inserting or deleting a chip?

After creating an autocomplete chiplist component in Angular, I noticed that when a user adds or removes an item, the suggestion list disappears and the input field is cleared. However, I would prefer to keep the autocomplete list open (or reopen it) so t ...

Generating Legible JavaScript Code from TypeScript

I am looking to maintain the readability of my compiled JS code, similar to how I originally wrote it, in order to make debugging easier. However, the typescript compiler introduces several changes that I would like to disable. For instance: During compi ...

.Net Core receives the method name instead of the parameter value passed by TypeScript

Can someone explain why the code is passing "getFullReport" as the eventId instead of the actual value while making its way to the .Net Core 3.1 side? Prior to the call, I double-checked with a console.log to ensure that eventId holds the correct ID I am ...

Ensuring Uniform Data Types Across Objects (Using Typescript)

After much trial and error, I have finally reached this point where everything seems to be working perfectly. function test<types extends Record<string,any>>(dict: dictionary<types>){} type dictionary<types extends Record<string, a ...

The 'api' property is not found within the 'MyService' type declaration

Currently, I am working on a tutorial where we are building an Angular service to send emails from a form using the Mailthis Email API. In the code for the service, I encountered an error related to the 'api' variable that states "Property ' ...

Spacing Problem with Title Tooltips

After using the padEnd method to ensure equal spacing for the string and binding in the title, I noticed that the console displayed the string perfectly aligned with spaces, but the binded title appeared different. Is it possible for the title to support s ...

Exploring Angular: Looping through an Array of Objects

How can I extract and display values from a JSON object in a loop without using the keyValue pipe? Specifically, I am trying to access the "student2" data and display the name associated with it. Any suggestions on how to achieve this? Thank you for any h ...