ProgrammingFrontend Developer

How is the typing mechanism of `this` in TypeScript class methods structured when using regular and arrow functions? Describe the pitfalls and best practices.

Pass interviews with Hintsage AI assistant

Answer.

In TypeScript, the default type of this for methods within a class is the current instance of the class. However, when defining methods using arrow functions, the context of this is bound to the time of declaration, not when called.

Regular Method:

class Counter { value = 0; increment() { this.value++; } }

Arrow Function:

class Counter { value = 0; increment = () => { this.value++; } }

Nuances:

  • Regular methods lose the this context when passed as callbacks (for example, in event listeners).
  • Arrow functions preserve the context of the declared class but are not visible in the prototype and are created for each instance.
  • You can explicitly specify the type of this in the method signature for additional checking:
class Foo { bar(this: Foo) { // ... } }

Trick Question.

What is the difference between increment() and increment = () => {} in a class? How does it affect the this context when used as a callback?

Incorrect Answer:

  • "There is no difference between the class field and the method; TypeScript understands everything."

Correct Answer:

  • For a regular method, the this context is determined at the time of call. If you pass the method as a function: setTimeout(counter.increment, 0), this becomes undefined (in strict mode) or window (in non-strict), while the arrow function retains its environment:
class Demo { value = 1; inc() { console.log(this.value); } incArrow = () => { console.log(this.value); } } const d = new Demo(); setTimeout(d.inc, 0); // undefined or error setTimeout(d.incArrow, 0); // 1

Examples of real errors due to misunderstanding the nuances of the topic.


Story

In a project with reactive frameworks, class methods were passed directly as callbacks without an explicit bind. As a result, this became undefined, and the application crashed with an error when accessing this properties. The problem was resolved by rewriting methods to use arrow functions or by using explicit bind.


Story

A developer explicitly specified the type of this in the method but forgot about the typing inside the arrow function within that method. This resulted in this inside the nested callback pointing not to the instance of the class, but to window. The team faced a state leak and had to redo the event architecture.


Story

A large UI component was slowing down the application because all methods were described as arrow class fields (new Counter().increment = ...), which created new function copies for each instance instead of a single definition on the prototype, as with regular methods. Consequently, memory consumption increased, and optimization was needed.