skip to content

In TypeScript, what does a first parameter literally named `this` declare in a function or method signature, and what does the compiler emit for it?

level: juniorimportance: should knowfreq 45%

answer

  1. a parameter that is not an argument
  2. must be first, erased on emit
  3. declares the required receiver
  4. not allowed on arrows or constructors
  5. this: void means detachable

basics

~20 s

A first parameter named this is a type-only declaration of the receiver the function requires. TypeScript checks call sites against it and then erases it: the emitted JavaScript has no such parameter and the function's arity is unchanged.

solid answer

~50 s

TypeScript lets you declare a fake first parameter named `this`, as in `function render(this: Widget, depth: number) {}`. It is not a real argument — it declares the type the receiver must have, so the compiler can reject a call that clearly cannot supply it, such as `const r = w.render; r()`. It must be the first parameter, it is excluded from the argument list everywhere else (`Parameters<T>` skips it and `fn.length` does not count it), and it is not allowed on arrow functions or constructors. Because the entire type layer is erased on emit, the output is just `function render(depth) {}` — no runtime check, no binding, no cost. A useful special case is `this: void`, which declares that the body never touches a receiver, so the function is safe to detach and pass around.

code

typescript · 15 lines
typescript
class Counter {
  count = 0;

  // `this` is a type-only parameter: it is erased from the emitted JS
  increment(this: Counter, by: number): void {
    this.count += by;
  }
}

const c = new Counter();
c.increment(1); // ok — the receiver is a Counter

const detached = c.increment;
// @ts-expect-error 'this' context of type 'void' is not assignable to 'Counter'
detached(1);

go deeper

for a junior

Be able to say that a parameter named this must come first, describes the required receiver, and disappears from the compiled output. Do not try to pass a value for it at the call site.

for a middle

Explain both checks the compiler performs — receiver compatibility at the call site and assignability between function types — and know that Parameters<T> and fn.length both ignore the this parameter.

for a senior

Show when the annotation earns its keep in real code: methods that get handed to schedulers, event systems or third-party callers, and callback parameter types declared this: void so a receiver-dependent method cannot be passed in.

for a principal

Own the API-shape decision. Argue when a codebase should model receivers in the type layer at all versus designing callbacks that take their context as an ordinary parameter, and be clear that the annotation is documentation the checker enforces, never a runtime guarantee.

## The problem it addresses In JavaScript, which object a function's `this` refers to is decided by how the function is *called*, not by where it was written — those binding rules are JavaScript's own and TypeScript cannot change them. What TypeScript adds is a way for a function to *declare* which receiver it needs, so the checker can complain about call sites that plainly will not supply it. ## The syntax and its rules A `this` parameter is written like an ordinary parameter, but the name `this` is reserved for this purpose and it must come first: ```ts interface Widget { depth: number } function render(this: Widget, indent: number): string { return " ".repeat(indent) + this.depth; } ``` The rules worth memorising: - It must be the **first** parameter; anything else is an error. - It is allowed on function declarations, function expressions, methods, method signatures in interfaces, and function type aliases such as `type Handler = (this: Widget, code: number) => void`. - It is **not** allowed on arrow functions (an arrow has no `this` of its own) or on constructors. - It is not part of the argument list: `Parameters<Handler>` yields only the real parameters, and the runtime `fn.length` counts only the real ones too. ## What the compiler actually checks Two things. First, the **call site**: when you call `w.render(2)` the receiver is `w`, which must be assignable to `Widget`. When you detach the method and call it bare, the receiver type is `void`, and that is not assignable to `Widget`: ```ts const detached = widget.render; detached(2); // error: 'this' context of type 'void' is not assignable to 'Widget' ``` Second, **assignability between function types**. A function declaring `this: Widget` is not assignable to a callback type declaring `this: void`, which is exactly how a library says "the callback I invoke gets no meaningful receiver, so do not hand me a method that needs one". ## It is erased Nothing survives compilation. The emit for the example above is: ```js function render(indent) { return " ".repeat(indent) + this.depth; } ``` No argument is inserted, no `bind` is generated, no check is performed. This matters for expectations: the annotation buys you compile-time errors and nothing else. If some untyped code calls `render.call(null, 2)`, the failure happens at runtime exactly as it would have without the annotation. ## `this: void` as a design tool Declaring `this: void` states that the body never reads `this`. Two consequences follow: the function can be detached and passed anywhere without risk, and attaching it to an object and calling it as a method becomes a type error. Library authors use it on callback parameter types so that consumers get an error when they pass a receiver-dependent method where a free function was expected. ## Where you meet it in the wild Mostly in declaration files for callback-style APIs written before arrow functions were common — APIs that invoke your callback with a meaningful receiver — and in your own code when a method will be handed to something else that calls it. In ordinary application code it is uncommon, because most teams sidestep the whole issue by using arrow functions or by passing the object explicitly. ## Mistakes to avoid The frequent one is believing the annotation *does* something at runtime — that it binds, or inserts a guard. It does not. The second is trying to pass it: `render(widget, 2)` is wrong, because `this` is not an argument; the receiver is supplied by how you call, for example `widget.render(2)` or `render.call(widget, 2)`. The third is reaching for it on an arrow function, which the compiler rejects outright.

  • Why can't an arrow function declare a `this` parameter?
    An arrow function has no receiver of its own — it uses whatever `this` its enclosing scope had. Declaring a required receiver for something that ignores the caller's receiver would be meaningless, so the compiler rejects it outright. If you need a receiver-typed function, use a `function` expression or a method instead.
  • Does declaring a `this` parameter change what `Parameters<T>` returns for that function type?
    No. `Parameters<T>` extracts only the real argument list, so `Parameters<(this: Widget, n: number) => void>` is `[n: number]`. To get at the receiver type you use `ThisParameterType<T>`, and `OmitThisParameter<T>` gives you the same function type with the receiver requirement stripped.
  • What does `this: void` buy an API author over simply omitting the `this` parameter?
    Omitting it leaves the receiver unconstrained, so a receiver-dependent method can be passed in and will silently misbehave. `this: void` makes that assignment a compile error and documents that the callback is invoked without a useful receiver. It also makes calling the function as a method an error, which catches accidental attachment.

saying these in an interview costs you the question

  • Thinks the this parameter is passed as a real argument
  • Believes it inserts a runtime check or binds the function
  • Puts it after other parameters
  • Tries to declare it on an arrow function
  • Assumes it changes the emitted JavaScript

context