skip to content

What does TypeScript's `noImplicitThis` compiler option report, and why do class methods and object-literal methods usually keep compiling when you turn it on?

level: middleimportance: should knowfreq 50%

answer

  1. the this-shaped sibling of noImplicitAny
  2. receiver would otherwise be any
  3. classes and object literals already know
  4. free-standing function reading this
  5. annotate, arrow, or restructure

basics

~20 s

noImplicitThis reports every use of this whose type would otherwise fall back to any. Class methods and object-literal methods are unaffected because the checker already knows their receiver, so only free-standing functions that read this must be annotated.

solid answer

~50 s

`noImplicitThis` is a checking flag: wherever `this` appears in a position where TypeScript cannot work out its type, it would silently be `any`, and the flag turns that into an error instead. It is part of the `strict` family, so most modern projects have it on. Class methods and property initializers are fine because inside a class `this` is the instance type (or the class itself in a static). Object-literal methods are fine because `this` is contextually the literal being written. What it does catch is a plain `function` that reads `this` with nothing to infer from — typically a callback written in `function` form. The fixes are to annotate the receiver with a `this` parameter, to use an arrow function so the surrounding scope supplies `this`, or to restructure so the value arrives as a normal parameter. Note the flag only forces you to *declare* the receiver; the actual checking comes from the `this` parameter you then write.

code

typescript · 26 lines
typescript
// compiled with noImplicitThis enabled

function bad() {
  // @ts-expect-error 'this' implicitly has type 'any'
  return this.value;
}

function good(this: { value: number }) {
  return this.value; // receiver is declared, so it is typed
}

const literal = {
  value: 1,
  read() {
    return this.value; // fine: `this` is contextually the literal
  },
};

class Box {
  value = 1;
  read() {
    return this.value; // fine: `this` is the instance type
  }
}

console.log(good.call({ value: 2 }), literal.read(), new Box().read(), bad);

go deeper

for a junior

Recognise the error text about this implicitly having type any, and know the two quick fixes: switch the function to an arrow, or declare a this parameter naming the receiver type.

for a middle

Explain precisely which positions already have a known receiver — class members, object-literal methods, contextually typed callbacks — and why a free-standing function does not. Be ready to say the flag ships inside strict.

for a senior

Talk through enabling it on an existing codebase: the burst of errors it produces on function-style callbacks, why this: any is not a fix, and how you triage between annotating, converting to arrows, and reshaping the API.

for a principal

Frame it as a policy choice. Argue what the flag is worth across a large repo relative to the churn it creates, and where you would instead standardise on APIs that never depend on a receiver so the question never arises.

## What the flag does `noImplicitThis` is the `this`-shaped sibling of `noImplicitAny`. TypeScript will sometimes fail to determine what `this` is in a given position; without the flag it quietly assigns the type `any`, and every property access off it is unchecked. With the flag on, the checker reports an error at that spot instead: `'this' implicitly has type 'any' because it does not have a type annotation`. The flag is included in the `strict` family, so a project created from a modern template already has it. Note what it does *not* do. It does not verify that the receiver is correct at any call site, and it does not affect emit. It only refuses to let an unknown receiver decay into `any`. ## Where `this` is already known Most code compiles unchanged under the flag, because the checker has plenty of positions where the receiver is not in doubt: - **Class methods, accessors and property initializers.** Inside an instance member, `this` is the instance type; inside a `static` member, it is the class's own type. - **Object-literal methods and shorthand methods.** The receiver is contextually the object literal being written, so `this.value` inside `{ value: 1, read() { return this.value } }` is typed. - **Functions with a contextual type that supplies a receiver.** If a callback parameter's declared type includes a `this` parameter, a `function` expression passed to it inherits that receiver type without you restating it. ## Where it bites The classic failure is a free-standing `function` that reads `this`: ```ts function area() { return this.width * this.height; // error under noImplicitThis } ``` Nothing tells the checker what `area` will be attached to, so the receiver would be `any`, and the flag rejects it. The same happens with a `function` expression handed to an API whose callback type says nothing about a receiver, and with a `function` assigned onto an object after the literal has been created — the assignment is not the same as writing the method inside the literal. ## The three ways out 1. **Declare the receiver.** Add a `this` parameter: `function area(this: { width: number; height: number }) { ... }`. This is the fix that also buys call-site checking, because now detached calls are errors. 2. **Use an arrow function.** An arrow takes `this` from the scope where it was written, so there is nothing implicit left to infer. This is the everyday fix inside class bodies. Which receiver an arrow ends up with is a JavaScript scoping question, not a type-system one. 3. **Restructure.** Often the cleanest answer is that the function should never have used `this`: pass the data in as an ordinary parameter and the question disappears. ## The relationship to `this` parameters It helps to see the two as a pair with different jobs. `noImplicitThis` is the *nag*: it refuses to let an unknown receiver slide. The `this` parameter is the *contract*: once written, it makes the checker validate every call site and every assignment of that function to another function type. Turning on the flag without writing any `this` parameters mostly just pushes people towards arrow functions; the checking value arrives with the annotations. ## Interview-worthy nuances - Because the flag lives in the `strict` family, enabling `strict` on a legacy codebase full of `function`-style callbacks produces a burst of these errors at once. Teams usually work through them file by file rather than turning the flag back off. - The flag is about *implicitness*, not correctness: annotating `this: any` silences it while giving up all safety, which is a real smell in a code review. - It has no effect at all on emitted JavaScript. Turning it on changes what the compiler will accept, never what runs.

  • Does `noImplicitThis` make a detached method call an error on its own?
    No. On its own it only refuses an unknown receiver where one would decay to `any`. Detaching a class method and calling it bare is still accepted unless that method declares a `this` parameter — the annotation is what makes the checker compare the call site's receiver against the required type.
  • Why does writing `this.count` inside an object literal's method compile, while assigning the same function onto the object afterwards does not?
    Inside the literal, the method gets a contextual receiver — the literal being written. A function created separately and assigned later has no such context at the point it is declared, so its `this` would be implicitly `any` and the flag reports it. Declaring a `this` parameter on that function fixes it.
  • Someone silences the error by annotating `this: any`. What would you say in review?
    It satisfies the flag while discarding every benefit: property access off the receiver is unchecked and no call site is validated. Either write the real receiver type, or convert to an arrow function, or pass the data as an ordinary parameter. `this: any` should be treated the same way as any other stray `any` in the codebase.

saying these in an interview costs you the question

  • Claims it also validates the receiver at every call site
  • Thinks class methods need explicit this annotations under the flag
  • Says it changes the emitted JavaScript
  • Silences it with this: any and calls it fixed
  • Confuses it with a runtime guard against wrong receivers

context