In my Class, I have the capability to accept inputs in the form of SVGElements or HTMLElements or an array containing these elements. I want to establish strong typing for this so that the methods in the class can accurately determine the type based on user input. This setup works well if we know the type during initialization. However, I'm facing challenges when it comes to adding more elements to the class after initialization without TypeScript being aware of it.
Consider this example:
type InputTypes = HTMLElement | SVGElement
function ensureArray<T>(input?: T | T[]): T[] {
if(!input) return []
return Array.isArray(input) ? input : [input];
}
class MyClass2<E extends InputTypes> {
els?: E | [E]
bar: E;
constructor(m1: MyClass<E>) {
this.els = m1.props.els
const ensured = ensureArray<E>(this.els)
this.bar = ensured[0]
}
}
class MyClass<E extends InputTypes>{
props: { els?: E | [E] }
m2: MyClass2<E>
constructor(props: { els?: E | [E] }) {
this.props = props
this.m2 = new MyClass2(this)
}
getBar() {
return this.m2.bar
}
public setBar = (els: E | [E]) => {
const ensured = ensureArray<E>(els)
this.m2.bar = ensured[0]
}
}
const element = document.getElementsByClassName("bla")[0]
var test1 = new MyClass({})
var foo1 = test1.getBar()
var bar1 = test1.setBar(element as HTMLElement)
var baz1 = test1.getBar() // Type is InputTypes instead of HTMLElement
var test2 = new MyClass({ els: element as HTMLElement })
var foo2 = test2.getBar()
var baz21 = test2.getBar() // HTMLElement, awesome, that is correct! but…
var bar2 = test2.setBar(element as SVGElement) // Type 'SVGElement' is missing the following properties from type 'HTMLElement' …
var baz22 = test2.getBar()
As shown in bar1
, I am adding an element of type HTMLElement. Therefore, I expect baz1
to reflect the type as HTMLElement only, instead of a general HTML or SVG type.
Similarly, in the second test, I initialize with an HTMLElement, and TypeScript correctly identifies that all the elements are of type HTMLElement, presenting baz21
as HTMLElement. However, if I try to add another element of type SVGElement, TypeScript raises an error because it should be able to handle both types now. Even baz22
remains as HTMLElement, although it should include both HTMLElement and SVGElement since both types are present in the array.
How can I update the types accordingly? Is it feasible to achieve this in TypeScript?
For further exploration, here is a Playground Thank you!