I am mapping an object of type A multiple times via a succession of functions.
For example:
- first a function with type
(item: A) => B - then a function with type
(item: B) => C - then a function with type
(item: C) => D
and out comes an object of type D. These functions are passed as an array, which in the example would have type [(item: A) => B, (item: B) => C, (item: C) => D].
An concrete example of this:
const mappings: MappingSequence<number, { duration: string }> = [
a => a * 60, // (a: number) => number
b => `${b} seconds`, // (b: number) => string
c => ({ duration: c }), // (c: string) => { duration: string }
];
where applying all 3 functions to the number 2 results in { duration: '120 seconds' }.
I am looking to express this generic type MappingSequence<A, Z>:
type Mapping<X, Y> = (item: X) => Y;
// This definition style won't work because there are infinitely many options
type MappingSequence<A, Z> =
[] | // 0-step mapping, only if A = Z
[Mapping<A, Z>] | // 1-step mapping
[Mapping<A, B>, Mapping<B, Z>] | // 2-step mapping, for any B
[Mapping<A, B>, Mapping<B, C>, Mapping<C, Z>] | // 3-step mapping, for any B and C
// etc., for arbitrary array lengths
// This style might work, but the recursion is tricky
type MappingSequence<A, Z> =
[Mapping<A, Z>] |
[Mapping<A, B>, ...MappingSequence<B, Z>]; // for any B
However, I've gotten stuck in multiple different attempts.
The key difficulty seems to be expressing "for any B". I was looking to do this with the infer keyword; however, then I need to have conditional types, which I found hard when having the generic over A and Z.
Any insights in how this can be achieved? Thanks in advance.