TypeScript function chaining, but I want to programmatically chain them.
Example class: chain.ts
class MyChain {
value: number = 0;
constructor() {
this.value = 0;
}
sum(args: number[]) {
this.value = args.reduce((s, c) => s + c, 0);
return this;
}
add(v: number) {
this.value = this.value + v;
return this;
}
subtract(v: number) {
this.value = this.value - v;
return this;
}
}
const mc = new MyChain();
console.log(mc.sum([1, 2, 3, 4]).subtract(5).value);
I see the number 5
on the console.
Now, I'm still fairly new to JavaScript and TypeScript, so I figured out that the function within this class is actually an element of an array of the instance of the class. Hence, I can do this:
console.log(mc["sum"]([1, 2, 3, 4]).value);
This indeed returns the number 10
.
Now, I'm confused as to how I'd chain this programmatically. For example (this is obviously not what I would want to do anyway and shows my boneheaded lack of understanding of JavaScript:
console.log(mc["sum"]([1, 2, 3, 4]).mc["subtract"](5).value);
Error:
Property 'mc' does not exist on type 'MyChain'.ts(2339)
Okay, in all honesty, I kind of intuitively knew that wasn't going to work. However, thinking about it, how would I go about accessing the elements of a multidimensional array in just about any reasonable language?
console.log(mc["sum"]([1, 2, 3, 4])["subtract"](5).value);
Bingo. This does the trick. But, this isn't really the solution I need. What I need is something like this:
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4] },
{ action: "subtract", operand: 5 },
];
And, to start, I'd like to try this:
console.log(mc[chainObj[0].action](chainObj[0].operand).value);
And consequently, generating a mechanism that would ultimately build something like this:
console.log(
mc[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](
chainObj[1].operand
).value
);
Hence, it seems to me that what I want is some way to generate this:
[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](chainObj[1].operand)
from this, with my chain object having one or many action/operand object sets:
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4] },
{ action: "subtract", operand: 5 },
];
Now, this is where my brain more or less shuts down. I am thinking that I need to generate a chain of string values, but they'll just be strings and won't really work as array indexes into the function as I want.
Why do I want to do this? Ultimately, I want to build a complex Yup schema object from a JSON object. I found this excellent post, but my core issue is I don't really understand how this code works.
At this point, I am able to parse out the way Vijay was able to solve his issue and mimic it, in a way. Here's working code for my example:
const mc = new MyChain();
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4, 5] },
{ action: "subtract", operand: 5 },
];
let myChain = {};
chainObj.forEach((o) => {
myChain = mc[o.action](o.operand);
});
console.log("myChain is", myChain["value"]);
Results in: myChain is 10
You're probably asking yourself, "What's your problem Dan?. You seem to have a solution in hand now." Yes, I guess I do, but I don't understand it. I'm basically copying and pasting code, marginally understanding it, and making changes that make it work.
My basic issue is I don't understand how this line of code works: myChain = mc[o.action](o.operand);
I get the general gist that it's calling the function based on the action and providing the data to the function via the operand. I'm a copy and paste code monkey. I want to be more than a monkey. Maybe a baboon or even ape. Hence, I want to understand what I've done. What doesn't make sense to me is how it's chaining it.
I thought maybe the secret was in the forEach function, but that doesn't seem to be it. Here is a simple test:
let p = 0;
const x = [1, 2, 3, 4];
x.forEach((y) => {
p = y;
});
console.log("p is", p); p is 4
What is the secret JavaScript magic that is happening under the hood that makes the myChain = mc[o.action](o.operand);
code actually chain my functions together rather than simply work one and the work the other. I'm just not seeing it.