Skip to content

Latest commit

 

History

History
193 lines (151 loc) · 7.15 KB

this-in-callbacks.md

File metadata and controls

193 lines (151 loc) · 7.15 KB

Item 69: Provide a Type for this in Callbacks if It's Part of Their API

Things to Remember

  • Understand how this binding works.
  • Provide a type for this in callbacks if it's part of your API.
  • Avoid dynamic this binding in new APIs.

Code Samples

class C {
  vals = [1, 2, 3];
  logSquares() {
    for (const val of this.vals) {
      console.log(val ** 2);
    }
  }
}

const c = new C();
c.logSquares();

💻 playground


const c = new C();
const method = c.logSquares;
method();

💻 playground


const c = new C();
const method = c.logSquares;
method.call(c);  // Logs the squares again

💻 playground


document.querySelector('input')?.addEventListener('change', function(e) {
  console.log(this);  // Logs the input element on which the event fired.
});

💻 playground


class ResetButton {
  render() {
    return makeButton({text: 'Reset', onClick: this.onClick});
  }
  onClick() {
    alert(`Reset ${this}`);
  }
}

💻 playground


class ResetButton {
  constructor() {
    this.onClick = this.onClick.bind(this);
  }
  render() {
    return makeButton({text: 'Reset', onClick: this.onClick});
  }
  onClick() {
    alert(`Reset ${this}`);
  }
}

💻 playground


class ResetButton {
  render() {
    return makeButton({text: 'Reset', onClick: this.onClick});
  }
  onClick = () => {
    alert(`Reset ${this}`); // "this" refers to the ResetButton instance.
  }
}

💻 playground


class ResetButton {
  constructor() {
    this.onClick = () => {
      alert(`Reset ${this}`); // "this" refers to the ResetButton instance.
    };
  }
  render() {
    return makeButton({ text: 'Reset', onClick: this.onClick });
  }
}

💻 playground


function addKeyListener(
  el: HTMLElement,
  listener: (this: HTMLElement, e: KeyboardEvent) => void
) {
  el.addEventListener('keydown', e => listener.call(el, e));
}

💻 playground


function addKeyListener(
  el: HTMLElement,
  listener: (this: HTMLElement, e: KeyboardEvent) => void
) {
  el.addEventListener('keydown', e => {
    listener(el, e);
    //           ~ Expected 1 arguments, but got 2
  });
}

💻 playground


function addKeyListener(
  el: HTMLElement,
  listener: (this: HTMLElement, e: KeyboardEvent) => void
) {
  el.addEventListener('keydown', e => {
    listener(e);
    // ~~~~~~~~ The 'this' context of type 'void' is not assignable
    //          to method's 'this' of type 'HTMLElement'
  });
}

💻 playground


declare let el: HTMLElement;
addKeyListener(el, function(e) {
  console.log(this.innerHTML);
  //          ^? this: HTMLElement
});

💻 playground


class Foo {
  registerHandler(el: HTMLElement) {
    addKeyListener(el, e => {
      console.log(this.innerHTML);
      //               ~~~~~~~~~ Property 'innerHTML' does not exist on 'Foo'
    });
  }
}

💻 playground