org.sonar.l10n.javascript.rules.javascript.S6565.html Maven / Gradle / Ivy
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.
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.
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:
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
class A {
foo(): A { // Noncompliant
return this;
}
bar = (): A => { // Noncompliant
return this;
};
}
Compliant solution
class A {
foo(): this {
return this;
}
bar = (): this => {
return this;
};
}
Resources
Documentation
- TypeScript Documentation -
this
Types
- Wikipedia - Fluent interface