Reputation:
I have a map like this:
const map = {
a: ClassA,
b: ClassB,
c: ClassC,
}
which maps to different classes. and I have this function, which is returning instance of a class mapped by a key in map object:
const conf = {};
type VersionMap = typeof map;
type Versions = keyof VersionMap;
const getter = <T extends Versions>(ver: T): VersionMap[T] => new map[ver](conf);
But problem is that typeof is referencing to class constructor, not an instance so I got an error. How can I solve that issue without creating second type?
Upvotes: 1
Views: 529
Reputation: 1926
I think there is some small misconception here on how classes are typed.
Let's assume, for abbreviation here, that we have two classes ClassA
and ClassB
.
class ClassA {
constructor() {
console.log("Class A");
}
}
class ClassB {
constructor() {
console.log("Class B");
}
}
Creating the map object is easy, but I think, it would have been easier to first describe an interface on what this object is supposed to hold. But as in your example, I proceed without one.
const map = {
a: ClassA,
b: ClassB,
};
Now comes the tricky part - Typings.
First I want to extract all keys of the map object.
Therefore I'll create a new type called: Keys
// Type contains: "a" | "b"
type Keys = keyof typeof map;
Next I need all associated values for each key.
To achieve this, create a new type at the index of Keys
.
// Type contains: typeof ClassA | typeof ClassB
type Values = typeof map[Keys];
The getter function should return an instance of a class T
. But the Values
type contains all classes as typeof ClassA | typeof ClassB
which is equal to new() => ClassA | new() => ClassB
.
Thus the type of ClassA | ClassB
is not compatible.
So let's craft a seperate type to get rid of this. The type should return the instance type of a class - just ClassA
or ClassB
.
Or so to say, the return type of new SomeClass()
operation.
type ReturnType<T> = T extends new() => infer R ? R : never;
If you want to read up on conditional types in TypeScript, I highly recommend reading this blog post: https://mariusschulz.com/blog/conditional-types-in-typescript Let's add the final piece:
const conf = {};
const getter = (ver: Keys): ReturnType<Values> => new map[ver]();
FYI: I did change the naming of VersionMap
to Values
and Version
to Keys
as I think it's easier to understand what each of those types is supposed to do. But feel free to change them to your needs.
Upvotes: 2