Skip to content

Scope of this is lost when passing a member function to setInterval #10285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
leewinder opened this issue Aug 11, 2016 · 10 comments
Open

Scope of this is lost when passing a member function to setInterval #10285

leewinder opened this issue Aug 11, 2016 · 10 comments
Labels
Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Good First Issue Well scoped, documented and has the green light Help Wanted You can do this Suggestion An idea for TypeScript
Milestone

Comments

@leewinder
Copy link

TypeScript Version: 1.8.10

When passing a member function to setInterval, the scope of 'this' is lost within the callback, though the structure of the code (given experience of any object orientated language) indicates it shouldn't be.

Example code

export class SetIntervalTest {
  private someNumber: number = 1;

  trigger() {
      setInterval(this.setIntervalCallback, 400);
  }

  private setIntervalCallback() {
      console.log(this.someNumber);
  }
}  

Expected behavior
When console.log(this.someNumber); is called, the scope of this is within the scope of the SetIntervalTest interval instance, printing '1'.

Actual behavior:
The scope of this is pulled from global scope, resulting in this.someNumber being 'undefined'. If that isn't possible, the compiler should indicate the expected behaviour is not what you're going to get.

To fix this i need to change 'setInterval(this.setIntervalCallback, 400);' to 'setInterval(() => this.setIntervalCallback(), 400);' so 'this' is correctly scoped in the callback.

@RyanCavanaugh
Copy link
Member

@leewinder
Copy link
Author

leewinder commented Aug 11, 2016

Thanks @RyanCavanaugh, still should be either caught at the compile stage (which is possible if it's expected behaviour) and flagged as a warning, or the generated JS structured should generate the obvious intent.

@RyanCavanaugh
Copy link
Member

See "something about this" in https://github.com/Microsoft/TypeScript/wiki/FAQ#common-feature-requests

You can have the "correct" code generated by using an arrow function:

    private setIntervalCallback = () => {
      console.log(this.someNumber);
    };

@kitsonk
Copy link
Contributor

kitsonk commented Aug 11, 2016

Also, note that this behaviour is consistent with the ES6 runtime behaviour. Methods are not statically bound to the instance.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 7, 2016

There are two parts to get this working correctelly:

  1. change the definition in lib.d.ts to include this: void:
declare function setInterval(handler: (this: void, ...args: any[]) => void, timeout: number): number;
  1. make methods have this type by default, this is tracked by This-function parameters must be specified in caller *and* callee, even for methods #10288, to work around this the definition of the method can be private setIntervalCallback(this: this) { ... }. with both these changes in you would see the error as desired.

@mhegazy mhegazy added the Suggestion An idea for TypeScript label Sep 7, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Sep 7, 2016

I would say the change to the library should be done either ways.

@mhegazy mhegazy added this to the TypeScript 2.1 milestone Sep 7, 2016
@mhegazy mhegazy modified the milestones: TypeScript 2.1, Future Sep 29, 2016
@gdh1995
Copy link
Contributor

gdh1995 commented Jan 26, 2017

I think here TypeScript should keep the same semantics just as ES6, but not force it bound by default.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Good First Issue Well scoped, documented and has the green light labels Aug 13, 2018
@RyanCavanaugh RyanCavanaugh modified the milestones: Future, Community Aug 13, 2018
@RyanCavanaugh RyanCavanaugh modified the milestones: Community, Backlog Mar 7, 2019
@zijianhuang
Copy link

@leewinder ,

setInterval(()=>this.setIntervalCallback(), 400);

should be just working fine so that the callback can see this.

@paskozdilar
Copy link

paskozdilar commented Jan 22, 2024

I think TypeScript should still at least print a warning (e.g. "Method passed without instance. Did you mean to use the arrow function? () => this.setIntervalCallback()"). Maybe with a specific comment that can turn off this warning in case the behavior is intentional.

ES6 runtime behavior specifies this, but in most cases, a naive programmer (like me) would expect this.setIntervalCallback() to be executed with the current value of this.

It took me a while to catch this error because it happened only in specific cases in my code where setTimeout() was called, so having TypeScript compiler warn about it would have prevented me from writing the bug in the first place.

@AudreyKj
Copy link

Is this issue still open? I'm happy to work on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Domain: lib.d.ts The issue relates to the different libraries shipped with TypeScript Good First Issue Well scoped, documented and has the green light Help Wanted You can do this Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

9 participants