Method chaining is an easy way to transform data in a more-readable way. It allows us to be more concise and expressive in our code. I have seen this pattern in libraries like D3.js, jQuery, Express/Koa, and testing libraries and I wanted to explore how I could use it in my own code.
I started with a simple calculator.
class Calculator {
public value: number;
constructor(value?: number) {
this.value = value || 0;
}
public add = (num: number) => {
this.value += num;
};
public subtract = (num: number) => {
this.value -= num;
};
public multiply = (num: number) => {
this.value *= num;
};
public divide = (num: number) => {
this.value /= num;
};
}
Using this class feels very verbose.
const calculator = new Calculator(42);
calculator.add(8); // 50
calculator.subtract(22); // 28
calculator.multiply(4); // 112
calculator.divide(2); // 56
A more expressive approach is to use method chaining and return the instance from each method.
class Calculator {
public value: number;
constructor(value?: number) {
this.value = value || 0;
return this;
}
public add = (num: number): Calculator => {
this.value += num;
return this;
};
public subtract = (num: number): Calculator => {
this.value -= num;
return this;
};
public multiply = (num: number): Calculator => {
this.value *= num;
return this;
};
public divide = (num: number): Calculator => {
this.value /= num;
return this;
};
}
This allows us to chain methods together.
const calculator = new Calculator(42).add(8).subtract(22).multiply(4).divide(2); // 56
We can use a HOF to wrap each method and return the instance.
class Calculator {
public value: number;
constructor(value?: number) {
this.value = value || 0;
return this;
}
private chainable = <T extends any[]>(
operation: (...args: T) => void
): ((...args: T) => Calculator) => {
return (...args: T): Calculator => {
operation.apply(this, args);
return this;
};
};
public add = this.chainable((num: number) => {
this.value += num;
});
public subtract = this.chainable((num: number) => {
this.value -= num;
});
public multiply = this.chainable((num: number) => {
this.value *= num;
});
public divide = this.chainable((num: number) => {
this.value /= num;
});
}
You can view for of this quick exploration on my GitHub