I want to create a Pipe interface in TypeScript, the only constraint is that when the pipe-through is done, I want it to return the same type as what came in:
Pipe.pour(liquid).through(somePipelines) => typeof liquid
This is how I currently approach this:
A Pipe interface that can run some stuff through an array of Pipelines:
interface IPipe<SomeLiquidType> {
liquid: SomeLiquidType,
pour: (liduid: SomeLiquidType) => IPipe<SomeLiquidType>
through: (pipelines: Array<(input: SomeLiquidType) => SomeLiquidType>) => SomeLiquidType,
}
And this is an implementation of the pipe interface
const Pipe: IPipe<unknown> = {
liquid: null,
pour(somethingToPour) {
this.liquid = somethingToPour;
return this;
},
through(pipelines) {
for (const pipeline of pipelines) {
this.liquid = pipeline(this.liquid)
}
return this.liquid;
}
}
The idea is that any object that implements this Pipe interface should be able to pour() any liquid through() an array of pipelines.
Now, I created some pipelines to try this out:
function addSugar( liquid: Juice ){
...
return liquid;
}
function addStrawberryFlavour( liquid: Juice ){
...
return liquid;
}
And then use the Pipe service
const juice = GetSodaWater();
Pipe
.pour(juice)
.through([addSugar, addStrawberryFlavour])
I am getting error similar to:
Type '(addSugar: Juice) => Juice' is not assignable to type '(input: unknown) => unknown'.
It is clear that (input: unknown) => unknown in the error above is coming from IPipe<unknown>, but I do not know how to fix this. I have tried IPipe<any> but that results in another loop of problems.
How do I implement this Pipe without these weird errors?
CodePudding user response:
I think the conceptual problem here is that after the pour call the pipe becomes a pipe of Juice. You could model this by making pour generic and returning a IPipe of a specific liquid that is returned.
interface IPipe<SomeLiquidType = unknown> {
liquid: SomeLiquidType,
pour: <SpecificLiquid extends SomeLiquidType>(liduid: SpecificLiquid) => IPipe<SpecificLiquid>
through: (pipelines: Array<(input: SomeLiquidType) => SomeLiquidType>) => SomeLiquidType,
}
const Pipe: IPipe = {
liquid: null,
pour<T>(somethingToPour: T) {
this.liquid = somethingToPour;
return this as IPipe<T>;
},
through(pipelines) {
for (const pipeline of pipelines) {
this.liquid = pipeline(this.liquid)
}
return this.liquid;
}
}
//../
Pipe
.pour(juice)
.through([addSugar, addStrawberryFlavour])
