A method that returns this keeps a fluent chain aware of the exact receiver type, including subclasses. Returning the base type instead breaks chaining as soon as a subclass adds a method, which is the difference between a builder that composes and one that does not.
Before you start
You should be comfortable with classes, inheritance and method signatures. This article covers polymorphic this types and this parameters; it assumes basic generic syntax.
Step-by-step walkthrough
Step 1: Return this, not the class name
Declaring where(clause: string): this makes the returned value keep the concrete type of the call site. A subclass call keeps the subclass type, so methods added by the subclass remain available after the call. Returning Query would narrow the chain back to the base type.
Step 2: Let subclasses extend the chain
Because this is polymorphic, a subclass method can also return this and the chain stays correct without redeclaring base methods. This is why this return types work with inheritance where a hard-coded class name does not.
Step 3: Constrain this for detached methods
A this parameter such as function clone(this: { id: string }) types how a method expects to be bound. It cannot appear in an arrow function, because arrows capture this lexically and have no binding to describe. Use it for helpers designed to be mixed into objects.
Worked scenario
The subclass keeps its own method after a base-chain call because the return type is this.
class Query {
private parts: string[] = [];
where(clause: string): this { this.parts.push(clause); return this; }
build(): string { return this.parts.join(' AND '); }
}
class PagedQuery extends Query {
limit(n: number): this { return this.where(`LIMIT ${n}`); }
}
const sql = new PagedQuery().where('a = 1').limit(10).build();
console.log(sql); // a = 1 AND LIMIT 10Walk through the example
where returns this, which is PagedQuery at this call site, so .limit is available after it. limit also returns this, so .build still resolves. The order proves the chain type is the subclass throughout; returning Query from where would have hidden .limit and caused a compile error.
Common mistake
Returning the base class name from a fluent method, which drops subclass methods from the chain. Another mistake is writing a fluent method as an arrow function while trying to use a this parameter; arrows cannot declare one.
Verify the behavior
Build the chain in subclass order and confirm it compiles and produces the expected string. Then change where to return Query and confirm .limit becomes a compile error. Add a this parameter to a plain function and call it with .call on a compatible object to confirm it type-checks.
Interview exercise
Why does a builder returning BaseBuilder break subclass chaining, while returning this does not?
Answer and reasoning
Returning BaseBuilder erases the subclass type: after the call the compiler only knows the base methods, so subclass-specific methods are unavailable. this is a special type that resolves to the actual receiver type at each call site, so in a subclass context it is the subclass, and the chain keeps every available method. It is the type-system way to say “return whatever you were called on”.
Continue learning
Compare with function variance and generic constraints. Read the TypeScript polymorphic this handbook and try the TypeScript interview questions.