this Types & Polymorphism
this is also a type
Section titled “this is also a type”Inside a class, this is not only a value — it is a type that means “the type of the current instance.” Crucially, in a subclass this refers to the subclass, not the class where the method was written. This is the polymorphic this type, and it makes fluent APIs work correctly across inheritance.
Fluent chaining with polymorphic this
Section titled “Fluent chaining with polymorphic this”A builder that returns this from each method lets calls chain. Typing the return as this (not the class name) means subclass methods stay chainable too.
class QueryBuilder { private parts: string[] = []; where(cond: string): this { // return type is `this`, not QueryBuilder this.parts.push(cond); return this; }}
class MySQLBuilder extends QueryBuilder { limit(n: number): this { return this; }}
new MySQLBuilder() .where("a = 1") // returns MySQLBuilder (because of `this`), so... .limit(10); // ...limit() is still available. ✅If where had been typed to return QueryBuilder, the chain would “forget” it was a MySQLBuilder and .limit() would fail. Polymorphic this preserves the real type through the chain.
this parameters
Section titled “this parameters”A function can declare a fake first parameter named this to type what this must be when the function is called. It is erased on emit and does not affect the real argument list — it exists purely to type-check the calling context.
interface Card { title: string; }
function render(this: Card) { return this.title.toUpperCase();}
// render(); // ❌ The 'this' context of type 'void' is not assignable...render.call({ title: "hi" }); // ✅ this is a Card hereThis catches a whole class of bugs where a method relies on a this it might not actually get.
The lost-this pitfall, in the types
Section titled “The lost-this pitfall, in the types”The classic JavaScript footgun — passing a method as a callback and losing its this — has a type-level story too. When you pass obj.method by reference, the this binding is dropped:
class Counter { count = 0; increment() { this.count++; } // relies on `this`}
const c = new Counter();const fn = c.increment;// fn(); // 💥 at runtime `this` is undefinedTypeScript can catch this when you opt in. The strictBindCallApply and (with a this parameter) the checker will flag an unbound method call. The idiomatic fixes are an arrow-function field (increment = () => { ... }, which binds this lexically) or binding at the call site (c.increment.bind(c)).