querySelector
может возвращать любой подтип Element
. На веб-странице это почти наверняка будет либо HTMLElement
, либо SVGElement
. Они несовместимы, и все же TypeScript, кажется, рад, что я присвоил результат querySelector
типу HTMLElement
и получил доступ к свойствам, специфичным для HTMLElement
. Почему не выдает ошибку?
Вот пример
// 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.info(foo.hidden);
}
Компилируется нормально, однако foo
на самом деле может быть SVGElement
.
Если я вручную задаю тип foo
как SVGElement
, то машинописный текст будет по праву жаловаться на доступ к свойству hidden
.
Почему TypeScript не уловил ошибочное предположение, что возврат querySelector
обязательно является HTMLElement
?
Сигнатура метода
querySelector<E extends Element = Element>(selectors: string): E | null;
Определив возвращаемый тип как HTMLElement
(см. Вывод типа), вы предоставили общий тип E
. Ваш код эквивалентен
const foo = document.querySelector<HTMLElement>('*'); // HTMLElement | null
Единственное ограничение состоит в том, что E
должно расширяться Element
.
Это верно. Это один из тех случаев, когда вы, по сути, говорите компилятору, что вы знаете лучше всех. Однако это не предотвратит проблемы во время выполнения.
В машинописном тексте нет ничего плохого.
Из lib.dom.d.ts
для определения querySelector
(доступ, удерживая Ctrl + щелчок мышью по имени функции в хорошей IDE)
Мы будем найдены.
...
querySelector<E extends Element = Element>(selectors: string): E | null;
...
означает querySelector
— это Общий, а по умолчанию — Element
тип. Он вернется Element
, если мы их не указали.
и мы могли бы явно указать аргументы generic.
const foo = document.querySelector<SVGElement>("*");//this will return SVGElement | null
if (foo) {
//use foo as SVGElement
}
или путем вывода аргумента универсального типа (не знаю, как вызвать машинописный текст, но в С++ есть аналогичный)
const foo:SVGElement | null = document.querySelector("*");//typescript will inference to document.querySelector<SVGElement>("*")
if (foo) {
//use foo as SVGElement
}
Итак, причина, по которой вы получаете HTMLElement
, заключается в том, что Typescript выводит тип от const foo: HTMLElement | null
до document.querySelector<HTMLElement>("*")
.
@ Фил Да. и мой ответ кажется вводящим в заблуждение. так что я уже обновил их.
Спасибо, вы имеете в виду, что я утверждаю, что он вернет
HTMLELement
(хотя*
вполне может соответствоватьSVGElement
), и TypeScript этим доволен? Но в сгенерированном JavaScriptquerySelector('*')
может вернуть неHTMLElement
, а затем доступ к его свойствам, специфичным дляHTMLElement
, может привести к ошибке... Я думал, что TypeScript хотя бы предупредит об этом. Я неправильно понимаю?