Att använda querySelector i Typescript: varför accepterar den returtypen HTMLElement?
querySelector kan returnera vilken subtyp av Element som helst. På en webbsida skulle detta nästan säkert vara antingen HTMLElement eller SVGElement. De två är inte kompatibla, och ändå verkar TypeScript vara nöjt med att jag tilldelar resultatet av querySelector till en HTMLElement-typ och får åtkomst till egenskaper som är specifika för HTMLElement. Varför ger det inget fel?
Här är ett exempel
// why no error on assuming it's going to be HTMLELement
// and not SVGELement or some other Element?
const foo: HTMLElement | null = document.querySelector("*");
if (foo !== null) {
console.log(foo.hidden);
}
Detta kompilerar utan problem, men foo kan faktiskt vara ett SVGElement.
Om jag manuellt anger foo's typ till SVGElement så kommer TypeScript med rätta att klaga på att man försöker komma åt hidden-egenskapen.
Varför fångar inte TypeScript det felaktiga antagandet att returvärdet av querySelector nödvändigtvis är ett HTMLElement?
Svar #1
Signaturen för metoden är
querySelector<E extends Element = Element>(selectors: string): E | null;
Genom att definiera den returnerade typen som HTMLElement (se Typinferens) har du angett den generiska typen E. Din kod motsvarar
const foo = document.querySelector<HTMLElement>('*'); // HTMLElement | null
Den enda begränsningen är att E måste utöka Element.
Svar #2
Inget fel med TypeScript. Från lib.dom.d.ts för definitionen querySelector (åtkomst genom att hålla Ctrl + musklick på funktionsnamnet med en bra IDE) kommer vi att hitta.
...
querySelector<E extends Element = Element>(selectors: string): E | null;
...
betyder att querySelector är generisk och standardtypen är Element. Den kommer att returnera Element om vi inte specificerar dem uttryckligen.
och vi kunde explicit generiska argument.
const foo = document.querySelector<SVGElement>("*");//this will return SVGElement | null
if (foo) {
//use foo as SVGElement
}
eller genom generisk argumentdeduktion (vet inte hur man kallar det i TypeScript men i C++ finns en liknande)
const foo:SVGElement | null = document.querySelector("*");//typescript will inference to document.querySelector<SVGElement>("*")
if (foo) {
//use foo as SVGElement
}
Så anledningen till att du får HTMLElement är eftersom Typescript härleder typen från const foo: HTMLElement | null till document.querySelector<HTMLElement>("*").
