this type
Return the current class type from a method so fluent chains keep working in subclasses.
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
| Parameter | Type | Description |
|---|---|---|
$this (return position) | this | Resolves 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)optional | other: this | Requires 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 Typeoptional | this is T | A 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 parameteroptional | this: T | A 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
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.
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.
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