I'm altering your example code to stop weird issues like the types A, B<string> and B<number> all being the same and not to call the constructors A or B with a "foo" argument they don't accept.
Even in example code, it's a bad idea to have empty classes or unused type parameters, as they often lead to unexpected results.
And since your CTorType<T> uses the (...args: any[]) parameter list, it forgets any constraints on the constructor parameters and allows unsafe calls. The implementation of foo() as you have it ends up calling the A and B constructors with "foo" as a parameter, when neither constructor accepts parameters. So instead I'll use an empty parameter list () and only call new ctor() inside of foo().
Like this:
// zero arg constructor to prevent unsafe calls
type CtorType<T> = {
new (): T;
};
type IWrapper<T> = {
value: T;
};
function foo<T>(ctor: CtorType<T>): IWrapper<T> {
return {
value: new ctor() // zero arg call
};
}
// make A not empty
class A {
a = "a";
}
// make B not empty and depend on T
class B<T> {
b: T | undefined = undefined;
}
This is just to get the example code to a place where the only issue going on is the question you're trying to solve.
Okay, moving on:
B<number> is a syntactical error; the compiler is interpreting that as the first part of a generic function call with a specified type argument, such as in B<number>(13) assuming B was something like function B<T>(x: T){}. But B isn't a callable function, it's a constructor, so you get that error. If it were callable, you'd then get the error that you're missing the call (i.e., "(" expected). There is a type named B<number>, but no value named B<number> (in TypeScript, values are not generic).
There isn't a short syntax to specify a generic parameter in a function or constructor without calling or newing it. One thing you can do is to widen the constructor from a generic (which can act on all values of the type parameter) to a concrete one (which can only act on a single value of the type parameter), like this:
const BNumber: new () => B<number> = B;
const b = foo(BNumber); // IWrapper<B<number>>
The first BNumber assignment works because B is in fact a new () => B<number> (as well as a new () => B<string> and a new () => B<Array<{a: string}>>, etc etc). Then when you call foo on BNumber, it returns an IWrapper<B<number>> as you desire.
If this is the kind of thing you're going to do with B often, you could make the widening into a function that you call, like this:
const BWidener = <T>(): new () => B<T> => B;
const bAlso = foo(BWidener<number>()); // IWrapper<B<number>>
const bString = foo(BWidener<string>()); // IWrapper<B<string>>
const bWhatever = foo(BWidener<Array<{ a: string }>>()); // IWrapper<B<{a: string;}[]>>
Or, if this is something you're only going to do once and don't mind using type assertions then you can do it in one line like this:
const bAsWell = foo(B as new () => B<number>); // IWrapper<B<number>>
Okay, hope that helps. Good luck!
Link to code