A fluent interface designates an object-oriented API relying on method chaining to improve code readability.
In such interfaces, methods return the object `this` to allow the caller to chain multiple method invocations.

[source,javascript]
----
class RichText {
    constructor(private readonly text: string) {}
    bold(): RichText {
        // [...]
        return this;
    }
    italic(): RichText {
        // [...]
        return this;
    }
}

const richText = new RichText('Hello, World!');
// Chaining methods bold() and italic().
console.log(richText.bold().italic());
----

To better support fluent interfaces when used with a hierarchy of classes,
TypeScript provides a special type `this` that refers dynamically to the type of the current class.

Methods returning `this` should thus use the corresponding special type `this`
instead of the class name in their signatures.

== Why is this an issue?

When a method return type is the declaring class name in a hierarchy of classes,
it is impossible to chain methods defined in the superclass with methods defined in subclasses.

[source,javascript]
----
enum Color {
    RED, BLUE, GREEN
}

class Shape {
    // The return type is the class name.
    move(x: number, y: number): Shape {
        // [...]
        return this;
    }
}

class Polygon extends Shape {
    fill(color: Color): Polygon {
        // [...]
        return this;
    }
}

const polygon = new Polygon();
polygon.move(1.0, 2.0).fill(Color.RED);
//                     ^^^^
//                     Property 'fill' does not exist on type 'Shape'.
----

== How to fix it

When a method declaration uses the special type `this` instead of the class name for its return type,
TypeScript will use the type of the object `this` instead of the method declaring class
and will accept to invoke methods defined in the object class:

[source,javascript]
----
enum Color {
    RED, BLUE, GREEN
}

class Shape {
    // The return type is now "this"
    move(x: number, y: number): this {
        // [...]
        return this;
    }
}

class Polygon extends Shape {
    fill(color: Color): this {
        // [...]
        return this;
    }
}

const polygon = new Polygon();
polygon.move(1.0, 2.0).fill(Color.RED); // No compilation error
----

=== Code examples

==== Noncompliant code example

[source,javascript,diff-id=1,diff-type=noncompliant]
----
class A {
  foo(): A { // Noncompliant
    return this;
  }
  bar = (): A => { // Noncompliant
    return this;
  };
}
----

==== Compliant solution

[source,javascript,diff-id=1,diff-type=compliant]
----
class A {
  foo(): this {
    return this;
  }
  bar = (): this => {
    return this;
  };
}
----

== Resources
=== Documentation
* https://www.typescriptlang.org/docs/handbook/2/classes.html#this-types[TypeScript - ``++this++`` Types]
* https://en.wikipedia.org/wiki/Fluent_interface[Wikipedia - Fluent interface]
