Learn why TypeScript throws TS2683 when 'this' lacks a type annotation in functions, and how to properly type the this parameter.
TS2683 fires when you use this inside a function and TypeScript's noImplicitThis option (part of strict mode) can't determine what this refers to. In JavaScript, the value of this depends on how a function is called, not where it's defined. TypeScript wants you to be explicit about what this is so it can type-check your property accesses.
This error commonly appears in standalone functions, event handlers, and callback functions where this could be anything.
// The general shape of the error:
// 'this' implicitly has type 'any' because it does not have a type annotation.When a function uses this but isn't a method on a class or object, TypeScript can't infer the type.
// ❌ Broken
function getFullName() {
return `${this.firstName} ${this.lastName}`
// ~~~~ Error: 'this' implicitly has type 'any'
// because it does not have a type annotation.
}// ✅ Fixed — add a 'this' parameter
interface Person {
firstName: string
lastName: string
}
function getFullName(this: Person) {
return `${this.firstName} ${this.lastName}`
}
const user = { firstName: "Grace", lastName: "Hopper", getFullName }
user.getFullName() // "Grace Hopper"When passing a function as a callback, the this context often changes depending on who calls it.
// ❌ Broken
class SearchComponent {
query: string = ""
constructor() {
const input = document.getElementById("search")
input?.addEventListener("input", function (event) {
this.query = (event.target as HTMLInputElement).value
// ~~~~~ Error: 'this' implicitly has type 'any'
})
}
}// ✅ Fixed — use an arrow function to capture 'this' from the class
class SearchComponent {
query: string = ""
constructor() {
const input = document.getElementById("search")
input?.addEventListener("input", (event) => {
this.query = (event.target as HTMLInputElement).value
})
}
}When you extract a method from an object and pass it around, it loses its this binding.
// ❌ Broken
const logger = {
prefix: "[APP]",
log(message: string) {
console.log(`${this.prefix} ${message}`)
},
}
const logFn = logger.log
logFn("Starting up")
// At runtime: 'this' is undefined (or window in non-strict JS)
// At compile time with noImplicitThis: error on 'this.prefix'// ✅ Fixed — bind the method or use an arrow function
const logger = {
prefix: "[APP]",
log(message: string) {
console.log(`${this.prefix} ${message}`)
},
}
// Option 1: bind the method
const logFn = logger.log.bind(logger)
logFn("Starting up") // "[APP] Starting up"
// Option 2: wrap in an arrow function
const logFn2 = (msg: string) => logger.log(msg)
logFn2("Starting up") // "[APP] Starting up"Use an arrow function. Arrow functions don't have their own this — they capture this from the enclosing scope. This is the simplest fix for callbacks and event handlers:
button.addEventListener("click", () => {
this.handleClick() // 'this' comes from the surrounding class
})Add a this parameter. TypeScript lets you declare this as the first parameter of a function. It's erased at compile time and doesn't affect how you call the function:
function formatEntry(this: LogEntry) {
return `[${this.level}] ${this.message}`
}Use .bind() to fix the context. When passing object methods as callbacks, bind them to their owner:
class Timer {
seconds = 0
start() {
setInterval(this.tick.bind(this), 1000)
}
tick() { this.seconds++ }
}Use class field arrow functions. In classes, define methods as arrow function properties to automatically bind this:
class Counter {
count = 0
increment = () => {
this.count++ // always refers to the Counter instance
}
}TS2683 is triggered by the noImplicitThis compiler option (enabled under strict mode). It fires when you reference this in a function where TypeScript can't determine the type of this. This happens in standalone functions, regular (non-arrow) callbacks, and methods that have been detached from their objects. TypeScript raises the error because without knowing the type of this, it can't verify that property accesses like this.name are valid.
The best fix depends on the scenario. For class method callbacks, use arrow functions or .bind() to preserve the this context. For standalone utility functions, add an explicit this parameter: function greet(this: User) { ... }. For object methods that get passed around, either wrap them in arrow functions or define them as arrow function properties in the first place.
The this parameter is a TypeScript-specific syntax where you declare this as the first parameter of a function to specify its type. It's purely a compile-time construct — TypeScript erases it from the emitted JavaScript. For example, function greet(this: User, greeting: string) compiles to function greet(greeting) in JavaScript. When you call the function, you don't pass an argument for this — it's determined by the calling context as usual.
Get the latest TypeScript tips, tutorials, and course updates delivered straight to your inbox.