Item 69: Provide a Type for this in Callbacks if It's Part of Their API
May 10, 2024 ยท View on GitHub
Things to Remember
- Understand how
thisbinding works. - Provide a type for
thisin callbacks if it's part of your API. - Avoid dynamic
thisbinding 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();
const c = new C();
const method = c.logSquares;
method();
const c = new C();
const method = c.logSquares;
method.call(c); // Logs the squares again
document.querySelector('input')?.addEventListener('change', function(e) {
console.log(this); // Logs the input element on which the event fired.
});
class ResetButton {
render() {
return makeButton({text: 'Reset', onClick: this.onClick});
}
onClick() {
alert(`Reset ${this}`);
}
}
class ResetButton {
constructor() {
this.onClick = this.onClick.bind(this);
}
render() {
return makeButton({text: 'Reset', onClick: this.onClick});
}
onClick() {
alert(`Reset ${this}`);
}
}
class ResetButton {
render() {
return makeButton({text: 'Reset', onClick: this.onClick});
}
onClick = () => {
alert(`Reset ${this}`); // "this" refers to the ResetButton instance.
}
}
class ResetButton {
constructor() {
this.onClick = () => {
alert(`Reset ${this}`); // "this" refers to the ResetButton instance.
};
}
render() {
return makeButton({ text: 'Reset', onClick: this.onClick });
}
}
function addKeyListener(
el: HTMLElement,
listener: (this: HTMLElement, e: KeyboardEvent) => void
) {
el.addEventListener('keydown', e => listener.call(el, e));
}
function addKeyListener(
el: HTMLElement,
listener: (this: HTMLElement, e: KeyboardEvent) => void
) {
el.addEventListener('keydown', e => {
listener(el, e);
// ~ Expected 1 arguments, but got 2
});
}
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'
});
}
declare let el: HTMLElement;
addKeyListener(el, function(e) {
console.log(this.innerHTML);
// ^? this: HTMLElement
});
class Foo {
registerHandler(el: HTMLElement) {
addKeyListener(el, e => {
console.log(this.innerHTML);
// ~~~~~~~~~ Property 'innerHTML' does not exist on 'Foo'
});
}
}