The example below is a raw representation of what’s going on in real code (I'm not working with HTML or front-end. The code below is a dummy code. Sorry if it does not make any sense from front-end perspective)
TL, DR
I’m struggling to specify a return type of method which will return an array of class instances of the class from which this method was called.
Back to dummy code:
PageElement.ts
class PageElement {
public element: HTMLElement
constructor(
public elementSelector: string,
public elementName: string,
public description: string,
) {
this.element = document.getElementById(this.elementSelector)!;
}
getAll() {
let foundElements = document.querySelectorAll(this.elementSelector);
let returnElements = [];
let constructor = this.constructor as any;
foundElements.forEach(
element => returnElements.push(
new constructor(
element.attributes['id'].value,
this.elementName + (returnElements.length + 1),
this.description
)
)
)
return returnElements;
}
}
Let’s say this class has lots of child classes, e.g.:
class InputElement extends PageElement {
constructor(
elementSelector: string,
elementName: string,
description: string) {
super(elementSelector,
elementName,
description
)
}
exclusiveInputMethod() {
// this method exist only on InputElement
}
}
There are two core issues I would like to fix
getAll() {
let constructor = this.constructor as any;
}
The logic above works fine in JS like new this.constructor(...) .
However, TS does not allow to do this with error: This expression is not constructable. Type 'Function' has no construct signatures.ts(2351)
So I need to do let constructor = this.constructor as any;. But this kills IntelliSense and all type safety.
Of course, you could say: “Just return an array of PageElement, duh”:
getAll() {
let foundElements = document.querySelectorAll(this.elementSelector);
let returnElements = [];
foundElements.forEach(
element => returnElements.push(
new PageElement(
element.attributes['id'].value,
this.elementName + (returnElements.length + 1),
this.description
)
)
)
return returnElements;
}
But here is where the problem with inheritance comes up:
class InputElement extends PageElement {
constructor(
elementSelector: string,
elementName: string,
description: string) {
super(elementSelector,
elementName,
description
)
}
exclusiveInputMethod() {
// do stuff
}
}
In case getAll will return an array of PageElement I will not be able to access the exclusiveInputMethod. Because when I’ll call InputElement.findAll() it will return PageElement[] instead of InputElement[].
I’ll summarize my questions:
What is the proper equivalent of new this.constructor for TS so it knows, that I’m creating an instance of this particular class?
The second question is (well, it’s actually the same as first):
How can I annotate (or make TS to infer return type properly) method getAll() so it always knows that the return type is an array of the class instance from which this method was called?