3

Is it possible in Typescript to implement an infinite Array (not a Tuple) with a type that depends on the previous element of the array?

Here's some pseudo-typescript code to give an example:

class B<T, U> {}

function foo<X, Y>(...args: [B<X, Z0>, B<Z0, Z1>, B<Z1, Z2>, ..., B<ZN, Y>]) {}

foo<string, number>(new B<string, number>, new B<number, boolean>, new B<boolean, number>); // Correct
foo<string, number>(new B<string, number, new B<number, boolean>); // Incorrect
foo<string, number>(new B<string, number>, new B<boolean, number>); // Incorrect

What should I replace the "[B<X, Z0>, B<Z0, Z1>, B<Z1, Z2>, ..., B<ZN, Y>]" with to make this work? Is this even possible?

Thank you!

Edouard
  • 31
  • 1
  • 1
    I'm inclined to close this as a duplicate of [this question](https://stackoverflow.com/questions/53173203/typescript-recursive-function-composition) but the question here isn't quite specific enough for me to tell what's going on. Your example `B` is an empty class with unused type parameters, thus it is impossible to have any code that accepts. say, `B` while rejecting `B`, since they are identical types equal to `{}`. `.Please consider modifying the code so as to constitute a [mcve] that can be dropped into a standalone IDE to demonstrate the issue to others. – jcalz Dec 19 '20 at 19:24

1 Answers1

2

I recently encountered a similar problem. My problem involved creating a long pipe of streams. The output of stream i had to be the input of stream i+1, etc. I solved it like this:

type OmitFirst<T extends any[]> = T extends [any, ...infer R] ? R : never
type GetValueOrDefault<Thing, Key, Default = never> = Key extends keyof Thing ? Thing[Key] : Default
type Pipe<Start, Finish, Inputs extends [Start, ...any[]], Outputs extends any[] = OmitFirst<Inputs>> = {
    [I in keyof Inputs]: DuplexConstructor<Inputs[I], GetValueOrDefault<Outputs, I, Finish>>
}

export function pipe<F, L, P extends [F, ...any[]]>(
    ...streams: [ReadableConstructor<F>, ...Pipe<F, L, P>, WritableConstructor<L>]
): Writable {
    const source = streams[0]
    const duplexes = streams.slice(1, -1) as Pipe<F, L, P>
    const sink = streams[streams.length - 1] as WritableConstructor<L>

    let tail = readable(source)
    duplexes.forEach((dp) => (tail = tail.pipe(duplex(dp))))
    return tail.pipe(writable(sink))
}
Tim
  • 231
  • 1
  • 3
  • 6