As mentioned above, your version works as-is in TypeScript 4.1 and above, after the introduction of support for recursive conditional types.
type VariadicAndWithTransform<T extends any[]> = T extends [infer F, ...infer R]
? SimpleTransform<F> & VariadicAndWithTransform<R>
: unknown; // no error
type Works = VariadicAndWithTransform<[{ a: 1 }, { b: 2 }, { c: 3 }]>;
/* type Works = SimpleTransform<{
a: 1;
}> & SimpleTransform<{
b: 2;
}> & SimpleTransform<{
c: 3;
}> */
There were workarounds that could trick the compiler into allowing types before 4.1, but they were not officially supported. If you need recursive conditional types, you should upgrade your version of TypeScript.
But for the type function desired here, you don't need a recursive type. And that's actually a good thing, because recursive types are more taxing on the compiler, and there are fairly shallow recursion limits. If you use the above version of VariadicAndWithTransform<T>
where T
has several dozen elements, you will see errors, even in TS4.1+:
type Three = [{}, {}, {}];
type Nine = [...Three, ...Three, ...Three];
type TwentySeven = [...Nine, ...Nine, ...Nine]
type UhOh = VariadicAndWithTransform<TwentySeven>; // error!
// -------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type instantiation is excessively deep and possibly infinite.
The non-recursive version is harder for a human being to understand, but it makes things easier for the compiler:
type VariadicAndWithTransform<T extends any[]> = {
[K in keyof T]: (v: SimpleTransform<T[K]>) => void
}[number] extends ((v: infer I) => void) ? I : never
It works by inferring in a conditional type from a union in a contravariant position (such as the parameter to a function), similarly to the UnionToIntersection<T>
type from the answer to this question.
You can verify that it behaves the same for the example above:
type Works = VariadicAndWithTransform<[{ a: 1 }, { b: 2 }, { c: 3 }]>;
/* type Works = SimpleTransform<{
a: 1;
}> & SimpleTransform<{
b: 2;
}> & SimpleTransform<{
c: 3;
}> */
And because it does not use recursion, it does not have problems dealing with longer tuples:
type StillWorks = VariadicAndWithTransform<TwentySeven>;
/* type StillWorks = { wrapped: {}; } */
Playground link to code