Recipe · Functions

How do I type "this" inside a JavaScript function?

  • Browser (no bundler)
  • @this
  • this
  • noImplicitThis
  • Checked with TypeScript 5.9

The problem My event handler is a normal function that uses this, and tsc says 'this' implicitly has type 'any'. How do I say what this is in JSDoc?

The short answer

Add @this {Type} to the function’s JSDoc. Here the handler is attached to buttons, so this is an HTMLButtonElement:

save-button.js save-button.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/**
 * @this {HTMLButtonElement}
 * @param {MouseEvent} event
 */
function toggleSaved(event) {
  const saved = this.classList.toggle("saved");
  this.textContent = saved ? "Saved" : "Save";
  if (event.shiftKey) this.blur();
}

for (const button of document.querySelectorAll("button")) {
  button.addEventListener("click", toggleSaved);
}
function toggleSaved(this: HTMLButtonElement, event: MouseEvent): void {
  const saved = this.classList.toggle("saved");
  this.textContent = saved ? "Saved" : "Save";
  if (event.shiftKey) this.blur();
}

for (const button of document.querySelectorAll("button")) {
  button.addEventListener("click", toggleSaved);
}
A function whose this is given with @this.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "lib": ["es2022", "dom", "dom.iterable"],
    "types": []
  },
  "include": ["*.js"]
}

Inside toggleSaved, this.classList and this.blur() are checked against the button type, and addEventListener accepts the function because its this matches the element the listener is added to.

Why it works

JavaScript decides this when a function is called, not when it is written, so the checker cannot work it out from the function alone. @this {HTMLButtonElement} is the JSDoc form of TypeScript’s fake first parameter, this: HTMLButtonElement: it types this in the body, and it is checked at every call, which must supply a matching this.

The same thing in TypeScript

The TypeScript equivalent tab declares function toggleSaved(this: HTMLButtonElement, event: MouseEvent). The this parameter is not a real argument; it disappears when the code runs, which is why JavaScript needs a JSDoc tag for it instead.

What goes wrong

Without the tag, strict (through noImplicitThis) reports the first use of this (TS2683):

save-button.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: save-button.js(4,17): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. save-button.js(4,17): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. save-button.js(4,17): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. checked 21 October 2026
/** @param {MouseEvent} event */
function toggleSaved(event) {
  const saved = this.classList.toggle("saved");error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
  return saved && event.shiftKey;
}

for (const button of document.querySelectorAll("button")) {
  button.addEventListener("click", toggleSaved);
}
A function that uses this without saying what it is.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "lib": ["es2022", "dom", "dom.iterable"],
    "types": []
  },
  "include": ["*.js"]
}

With the tag, the calls are checked instead. A plain call supplies no this at all, so it fails with TS2684:

save-button.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: save-button.js(11,1): error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'HTMLButtonElement'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. save-button.js(11,1): error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'HTMLButtonElement'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. save-button.js(11,1): error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'HTMLButtonElement'. checked 21 October 2026
/**
 * @this {HTMLButtonElement}
 * @param {MouseEvent} event
 */
function toggleSaved(event) {
  this.classList.toggle("saved");
  return event.shiftKey;
}

toggleSaved(new MouseEvent("click"));error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'HTMLButtonElement'.
Calling a @this function without its this.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "lib": ["es2022", "dom", "dom.iterable"],
    "types": []
  },
  "include": ["*.js"]
}

Call it with toggleSaved.call(button, event), or let addEventListener call it, and the error goes away.

Gotchas

  • @this belongs to normal functions. On an arrow function it is an error (TS2730, “An arrow function cannot have a ‘this’ parameter”), because arrow functions never get a this of their own.
  • The element type must match. button.addEventListener("click", toggleSaved) passes because the button is an HTMLButtonElement. Adding the same listener to a <div>, or to the plain Element that querySelector(".save") returns, fails with TS2769; narrow the element with instanceof HTMLButtonElement first. That check leans on the typed event parameter: a handler with no parameters also matches addEventListener’s catch-all signature, and the wrong element goes unreported (the @this page shows that case).
  • A constructor function is different. A function that assigns to this.x is treated as a constructor, so its this is never an implicit any; a class is the clearer way to write it.

Questions

Do methods in a class or an object literal need @this?

No. In a class method this is the instance, and in a method of an object literal it is that object; tsc knows both. @this is for standalone functions whose this is set by the caller, such as event handlers and functions borrowed with call.

Should I use an arrow function instead?

An arrow function has no this of its own; it uses the surrounding one. For a handler that needs the element, a normal function with @this works, or an arrow function that takes the element from event.currentTarget.

Sources

Last reviewed 21 October 2026.