Typescript: Scope of this is lost when passing a member function to setInterval

Created on 11 Aug 2016  ·  8Comments  ·  Source: microsoft/TypeScript

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.

lib.d.ts Suggestion good first issue help wanted

Most helpful comment

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);
    };

All 8 comments

https://github.com/Microsoft/TypeScript/wiki/FAQ#why-does-this-get-orphaned-in-my-instance-methods

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.

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);
    };

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

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 https://github.com/Microsoft/TypeScript/issues/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.

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

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

@leewinder ,

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

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

weswigham picture weswigham  ·  3Comments

fwanicka picture fwanicka  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments

blendsdk picture blendsdk  ·  3Comments