Home > Software design >  How to specify implied types in TypeScript
How to specify implied types in TypeScript

Time:01-21

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])

Playground Link

  •  Tags:  
  • Related