[FIXED] Use class as TypeScript parameter for Map

Issue

I found some other similar questions answered on SO, but none of them seems to match my issue. I’ve made a TypeScript playground to work on it.

Here is my code:

type MConstructor<T extends new(...args: any[]) => any> = new (...args: ConstructorParameters<T>) => InstanceType<T>;

const componentData = new Map<MConstructor<any>, Map<HTMLElement, InstanceType<MConstructor<any>>>>();

const Data = {
  set: <T extends MConstructor<T>>(element: HTMLElement, component: T, instance: InstanceType<T>): void => {
    if (!(element instanceof HTMLElement)) return;

    if (!componentData.has(component)) {
      componentData.set(component, new Map<HTMLElement, InstanceType<T>>());
    }

    const instanceMap = componentData.get(component) as Map<HTMLElement, InstanceType<T>>;

    instanceMap.set(element, instance);
  },
};

class Alert {
  target: HTMLElement;
  ops: {} = {};
  constructor(target: HTMLElement, ops: {} = {}) {
    this.target = target
    this.ops = ops
    Data.set<Alert>(target, Alert, this)
  }
  add(target:HTMLElement) {this.target = target}
}

new Alert(document.createElement('div'), {op1: true, op2: 'op2_value'})

Here’s the error:

Type 'Alert' does not satisfy the constraint 'MConstructor<Alert>'.
  Type 'Alert' provides no match for the signature 'new (...args: never): any'.

Update: on a closer look, seems I can actually do Data.set(target, Alert, this) and the error goes away, but I’m not sure it’s a consistent TypeScript implementation.

How can I solve this?

Solution

It should be like the following snippet. The thing is that classes are values and you need to get its type instead.

Data.set<typeof Alert>(target, Alert, this)

Or even without the generic argument by letting Typescript infer it automatically:

Data.set(target, Alert, this)

Answered By – lepsch

Answer Checked By – Clifford M. (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published