For my specific use case (transforming variables within an Array) I ended up having to use a more sophisticated approach, using the techniques described here: https://medium.com/free-code-camp/typescript-curry-ramda-types-f747e99744ab
/* eslint-disable @typescript-eslint/no-explicit-any */
type Length<T extends any[]> = T['length']
type Cast<X, Y> = X extends Y ? X : Y;
type Prepend<E, T extends any[]> =
((head: E, ...args: T) => any) extends ((...args: infer U) => any)
? U
: T
type Pos<I extends any[]> =
Length<I>;
type Next<I extends any[]> =
Prepend<any, I>;
type Reverse<
T extends any[],
R extends any[] = [],
I extends any[] = []
> = {
0: Reverse<T, Prepend<T[Pos<I>], R>, Next<I>>
1: R
}[
Pos<I> extends Length<T>
? 1
: 0
]
type Concat<T1 extends any[], T2 extends any[]> =
Reverse<Reverse<T1> extends infer R ? Cast<R, any[]> : never, T2>;
type Append<E, T extends any[]> =
Concat<T, [E]>;
export type InstanceTypes<
T extends { new(...args: any): any}[],
R extends any[] = [],
I extends any[] = []
> = {
0: InstanceTypes<
T,
Append<InstanceType<T[Pos<I>]>, R> extends infer U
? Cast<U, any[]>
: never,
Next<I>
>
1: R
}[
Pos<I> extends Length<T>
? 1
: 0
];
InstanceTypes correctly converts [typeof A, typeof B, ...] to [A, B, C] as I require.
query = Query(Foo, Bar)-- used this way,Queryis not a constructor but a regular function. In order to be a constructor it has to be used usingnew. From the OOP point of view, an object is a smart component, not just a dumb container. If the constructor accepts any arguments then your "class" is not a class, it is just a convoluted way to implement a dumb container.