this type

Return the current class type from a method so fluent chains keep working in subclasses.

Signature
class Box {
  set(value: string): this;          // polymorphic return type
  sameAs(other: this): boolean;      // parameter position
  isFile(): this is FileRep;         // this-based type guard
}
function f(this: HTMLElement, e: Event): void;   // this parameter
  • Added in TypeScript 1.7

Parameters

ParameterTypeDescription
$this (return position)thisResolves to the type of the value left of the dot at the call site, so a subclass instance stays a subclass instance through a chain.
$this (parameter position)optionalother: thisRequires an argument of the same class as the receiver. Stricter than naming the class: a subclass's method will only accept instances of that subclass.
$this is Typeoptionalthis is TA type predicate in return position for a method of a class or interface. A truthy result narrows the receiver to T at the call site.
$this parameteroptionalthis: TA fake first parameter that types the `this` context of a standalone function or method. Erased at compile time and not counted as an argument.

Return value

thisThe type of the receiver — the class the method was actually called on, not the class it was declared in.

Errors

tsc reports "Type 'Box' is not assignable to type 'this'" when a method declared to return `this` returns a freshly constructed instance, and "The 'this' context of type 'void' is not assignable to method's 'this' of type 'T'" when a method typed with a `this` parameter is called detached from its receiver.

Changelog

  • PHP 2.0`this` parameters added, letting a function declare the type of its `this` context, along with `noImplicitThis` to flag untyped ones.
  • PHP 2.0`this is Type` guards usable in return position on class and interface methods.
  • PHP 3.0`this` in an object literal method is inferred from the surrounding literal, so fluent builders written as plain objects also chain.

Inside a class, this is a type as well as a value: it means "the type of the left side of the dot in a method call". Annotating a method's return type as this makes it polymorphic — call it on a subclass instance and you get the subclass type back, not the base class.

Why chains break without it

Write from(table: string): QueryBuilder and a chain through a subclass loses the subclass. new PagedBuilder().from("users").limit(10) fails, because from declared it returns the base and limit does not exist there. Return this instead and each step preserves the real type all the way down. TypeScript infers this for a method that returns this without an annotation, so often you get the behaviour for free — but write it explicitly on any public builder API so a later refactor cannot silently widen it.

The two other positions

As a parameter type, other: this is stricter than other: Box: a subclass method will only accept siblings of the same subclass. As a return type predicate, this is FileRep lets a method narrow its own receiver at the call site — the idiomatic way to write isFile(), isDirectory() or a lazy hasValue() validation check.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Keep a fluent query, request or validation builder chainable after a subclass adds methods.
  • Write `isX(): this is X` guards so callers can narrow an instance without `instanceof` at every site.
  • Require a comparison method to receive an instance of the same concrete class, not merely the base.
  • Type the `this` context of a callback or a standalone helper with a `this` parameter under `noImplicitThis`.

Frequently asked questions

Why does my method chain break after subclassing?

Because the base class methods declare the base class as their return type, so the chain forgets it is holding a subclass. Annotate those methods `: this` and return the receiver, and each call preserves the concrete type through the whole chain.

What is the difference between `this` and the class name as a type?

The class name is fixed; `this` resolves to whatever type the method was called on. In return position that keeps subclasses chainable, and in parameter position it is stricter, requiring an argument of the same concrete class rather than any subclass of the base.

How do I narrow an object using one of its own methods?

Declare the method's return type as a `this` type predicate, such as `isFile(): this is FileRep`, and return a runtime check. When the call is truthy TypeScript narrows the receiver to that type at the call site, which is cleaner than an `instanceof` check scattered through the callers.

More examples

`this is Type` guards narrowing the receiver

Prints "a.txt file 120 bytes" then "src dir 0 children". Each element is typed `FsNode`, which has neither `bytes` nor `children`. The `this is FileNode` predicate narrows the receiver inside the `if`, so `n.bytes` resolves without a cast. Change the return type to plain `boolean` and both property accesses stop compiling — the predicate is doing all the work.

Example · typescript
class FsNode {
  constructor(public path: string) {}
  isFile(): this is FileNode { return this instanceof FileNode; }
  isDir(): this is DirNode { return this instanceof DirNode; }
}
class FileNode extends FsNode {
  constructor(path: string, public bytes: number) { super(path); }
}
class DirNode extends FsNode { children: string[] = []; }

const items: FsNode[] = [new FileNode("a.txt", 120), new DirNode("src")];
for (const n of items) {
  if (n.isFile()) console.log(n.path, "file", n.bytes, "bytes");
  else if (n.isDir()) console.log(n.path, "dir", n.children.length, "children");
}

`this` in a parameter position is stricter than the class name

Prints "true" then "true". The commented line is the point: at runtime `a.equals(c)` would return true and quietly compare dollars to euros, but `other: this` resolves to `Usd` on a `Usd` receiver and rejects it at compile time. Writing `other: Money` instead would have accepted the mismatched currencies.

Example · typescript
class Money {
  constructor(public amount: number) {}
  // `other: this` means "same concrete class as the receiver".
  equals(other: this): boolean { return other.amount === this.amount; }
}
class Usd extends Money {}
class Eur extends Money {}

const a = new Usd(10);
const b = new Usd(10);
const c = new Eur(10);

console.log(a.equals(b));       // ok: both Usd
// console.log(a.equals(c));    // tsc: Eur is not assignable to Usd
console.log(new Money(5).equals(new Money(5)));

Discussion

  • Be the first to comment on this lesson.