Reputation: 16597
I want to use typeof
operator to get the constructor type from class, but it doesn't seem to work when you want to use it on a dictionary of classes. E.g. typeof Modules[T]
- instead of getting the type of Modules[T]
, the compiler thinks of (typeof Modules)[T]
class A { }
class B { }
abstract class Modules {
public a!: A;
public b!: B;
}
// Error: Type 'T' cannot be used to index type 'typeof Modules'
function addModule<T extends keyof Modules>(key: T, ModuleCtor: typeof Modules[T]) {
let module = new ModuleCtor();
}
addModule('a', A);
// Works but the constructor arguments type is lost
function addModule2<T extends keyof Modules>(
key: T,
ModuleCtor: new (...args:any[]) => Modules[T]
) {
let module = new ModuleCtor();
}
addModule2('a', A);
As Aleksey pointed out, typeof
operator works only on values. Classes are actual values, but what I have here is pure types. I'm essentially looking for something that works like typeof Class (value)
that would get the exact type of constructor (including its arguments) of a pure class type.
Upvotes: 4
Views: 4100
Reputation: 24181
Your first problem is that you have to remember Typescript types are based on the Shape of an interface, there not Hard Types.
So in your example -> class A { } and class B { } are the same Type..
So the first thing we need to do is in your example is make A & B different somehow, so that Typescript can know the difference.
Otherwise you could do -> addModule('a', B);
and I'm assuming you want to trap that.
eg.
class A { A?:string }
class B { B?:string }
In the above, class A & B types are now different, in a real application it's likely that your methods etc are different, so doing a dummy A?:string
wouldn't normally be required.
The next problem is we need to define the shape of our constructor, if your constructor is empty, a simple example would be ->
type Construct<T> = new () => T;
After doing this our final result is ->
class A {
A?: string
}
class B {
B?: string
}
class Modules {
a!: A;
b!: B;
}
type Construct<T> = new () => T;
function addModule<T extends keyof Modules, C extends Construct<Modules[T]>>(key: T, ModuleCtor: C) {
const x = new ModuleCtor()
}
var a = addModule('a', A)
//Argument of type 'typeof B' is not assignable to parameter of type 'Construct<A>'.
var a = addModule('a', B)
Upvotes: 3