1

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?

1
  • Have you checked with specific with dot operator like map.a, map.b etc?. Because just map would give the whole thing? Commented May 15, 2020 at 9:17

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.