Fix TypeScript Error: 'this' implicitly has an 'any' type in Callbacks and Event Handlers

intermediate๐Ÿ”ต TypeScript2026-05-06| TypeScript 2.0+, Node.js, Browser (DOM), any project with strict or noImplicitThis enabled in tsconfig.json

Error Message

'this' implicitly has an 'any' type.
#typescript#this#callback#noImplicitThis#event-handler

TL;DR

TypeScript can't figure out what this refers to inside your function. Three quick exits:

  • Switch to an arrow function โ€” it inherits this from the surrounding scope automatically.
  • Declare a typed this parameter as the first argument of your function.
  • Use .bind(this) after giving the method an explicit this type.

When does this error appear?

The error fires when noImplicitThis (or strict mode) is enabled in tsconfig.json and TypeScript spots a this it can't statically resolve. Four situations trigger it constantly:

  • A regular function used as a DOM event listener
  • A standalone callback passed to forEach, setTimeout, etc.
  • A class method extracted and used as a callback elsewhere
  • Object literal methods that reference this inside nested functions
// tsconfig.json
{
  "compilerOptions": {
    "strict": true  // enables noImplicitThis among other checks
  }
}

Root cause

JavaScript's this is determined by how a function is called, not where it's defined. That's the core problem. TypeScript can't trace runtime call sites, so when you write a plain function and use this inside, the compiler has no idea what object it'll be bound to. It refuses to guess.

// โŒ Error: 'this' implicitly has an 'any' type.
document.querySelector('button')?.addEventListener('click', function () {
  console.log(this.textContent); // TypeScript: what is 'this' here?
});

// โŒ Same problem in a plain callback
const items = [1, 2, 3];
items.forEach(function (item) {
  console.log(this); // Error: 'this' implicitly has an 'any' type.
});

Fix 1: Use an arrow function (most common fix)

Arrow functions have no this of their own. They grab it from the enclosing lexical scope โ€” which is exactly what you want inside a class method.

class FormHandler {
  private label = 'Submit';

  init() {
    document.querySelector('button')?.addEventListener('click', () => {
      // โœ… 'this' refers to the FormHandler instance
      console.log(this.label);
    });
  }
}

Callbacks that don't use this at all? Arrow functions eliminate the ambiguity there too:

// โœ… Clean โ€” no 'this', no problem
[1, 2, 3].forEach((item) => {
  console.log(item);
});

Fix 2: Add an explicit this parameter

TypeScript supports a fake first parameter named this. It lets you declare the type without touching the function's real signature โ€” the compiler strips it entirely during compilation.

// โœ… TypeScript now knows exactly what 'this' is
function handleClick(this: HTMLButtonElement, event: MouseEvent) {
  console.log(this.textContent);
}

document.querySelector('button')?.addEventListener('click', handleClick);

This pattern shines when a handler is reused across multiple call sites. TypeScript enforces that callers bind it to the declared type โ€” mistakes get caught at compile time, not in production.

Fix 3: Explicit this in object literal methods

Here's a sneaky variant: a method in an object literal that passes a nested function as a callback. The outer method has correct context; the nested function loses it entirely.

// โŒ Nested function loses 'this'
const counter = {
  count: 0,
  start() {
    setInterval(function () {
      this.count++; // Error: 'this' implicitly has an 'any' type.
    }, 1000);
  }
};

// โœ… Arrow function inherits 'this' from start()
const counter = {
  count: 0,
  start() {
    setInterval(() => {
      this.count++; // 'this' is the counter object
    }, 1000);
  }
};

Fix 4: Use .bind() with an explicit this type

Working with third-party code or older patterns? .bind() works, but TypeScript loses type information across bare .bind() calls. The solution: declare the this type on the method first, then bind.

class Tooltip {
  message = 'Hello';

  show(this: Tooltip) {
    console.log(this.message);
  }

  register() {
    // โœ… Safe โ€” 'show' already carries an explicit 'this' type
    document.addEventListener('mouseover', this.show.bind(this));
  }
}

Fix 5: Disable noImplicitThis for legacy code (last resort)

Migrating a large JavaScript codebase to TypeScript? You can't always fix 200 files before shipping. A temporary escape hatch exists โ€” just don't treat it as permanent.

// tsconfig.json โ€” only if you have a migration plan
{
  "compilerOptions": {
    "strict": true,
    "noImplicitThis": false
  }
}

Track which files still need fixing (TypeScript's // @ts-check comments help), and re-enable the flag once the migration is done.

Verification

Run the compiler in check-only mode โ€” no output files generated, just error reporting:

# Check one file
npx tsc --noEmit src/your-file.ts

# Check the whole project
npx tsc --noEmit

No output means clean (exit code 0). Still seeing the error? Check scope carefully. A very common mistake: you fixed the outer function but left a nested function keyword inside it โ€” that inner function reintroduces the ambiguity.

Quick decision guide

  • Inside a class method callback โ†’ use an arrow function
  • Standalone reusable handler โ†’ add explicit this: SomeType parameter
  • Object literal with nested callbacks โ†’ arrow function for the nested callback
  • Legacy codebase mid-migration โ†’ set noImplicitThis: false temporarily, track what's left

Related Error Notes