Допустим, у меня есть следующий обработчик событий (в React):
onClick = {(e: MouseEvent<HTMLElement>) => {
const link = e.target.href;
Я понимаю, почему TypeScript жалуется на приведенный выше код: e.target
может быть тегом <a>
(в этом случае он будет иметь href
), или это может быть какой-то другой элемент, у которого нет href
, и если этого не сделать, все может сломаться. т.
Но когда я использую необязательный оператор цепочки, не имеет значения, есть ли у e.target
href
или нет:
onClick = {(e: MouseEvent<HTMLElement>) => {
const link = e.target?.href;
То же самое относится и к этой (более подробной) версии:
onClick = {(e: MouseEvent<HTMLElement>) => {
if (!e.target.href) return;
const link = e.target.href;
Но несмотря ни на что, TypeScript выдает мне ошибку Property 'href' does not exist on type 'EventTarget'.ts(2339)
.
Как я могу выразить: «Мне все равно, есть ли у e.target
href
или нет» (без использования @ts-ignore
)?
Для этого не требуется React, но контекст важен (невозможно иметь MouseEvent без какой-либо библиотеки DOM, такой как React), поэтому я включил код React. И необязательная цепочка — это не отвлекающий маневр, в этом вся суть вопроса!
Вопрос в том, что когда Typescript знает, что у вас есть объект (например, объект MouseEvent
), и вы знаете, что этот объект может иметь свойство (а может и не иметь), например href
, как вы сообщите Typescript, что «у него может быть это свойство href
( тот, который я дополнительно связываю)", чтобы это не вызывало ошибки.
Необязательная цепочка связана с тем, что e.target
сам по себе является null
или undefined
, а не с тем, имеет ли он свойство href
или нет, так что да, это отвлекающий маневр. У вас есть различные варианты, например. для начала используйте более конкретный тип события или сузьте значение, которое вы получаете, до одного.
@Джаред Смит Да; Я отмечу.
TL;DR, компилятор не позволяет вам проверять тип во время выполнения таким образом, потому что вы делаете опасное предположение, что, поскольку свойство присутствует и не имеет нулевого значения, оно является тем, что вы думаете...
(Также просто не размещайте обработчики событий щелчка на якорных элементах, используйте настоящую кнопку.)
В данном конкретном случае это не вариант (использование делегированных событий для обработки событий, происходящих из div
полной визуализированной уценки)
Typescript жалуется, потому что интерфейс HTMLElement не имеет атрибута href
. Это связано с тем, что этот интерфейс представляет все возможные элементы DOM и имеет их общие свойства.
Вместо этого вам следует использовать HTMLAnchorElement.
Если вы не можете определить e: MouseEvent<HTMLAnchorElement>
, вы можете привести функцию позже, используя const target = e.target as HTMLAnchorElement
.
Обратите внимание, что этот подход безопасен, только если вы уверены, что e.target
— это HTMLAnchorElement
.
Если вы лечите дженерики HTMLElement
, вы можете использовать защиту типа :
const target = e.target
if ("href" in target) {
// here you can use `target.href`
}
Или, если вам нужна специальная функция
const isAnchorElem = (target): target is HTMLAnchorElement => "href" in target
const target = e.target
if (isAnchorElem(target)) {
// here `target` will be of type `HTMLAnchorElement`
}
Это имеет смысл, но это не всегда будет элемент привязки: событие просто настроено на срабатывание при каждом щелчке по элементу, и хотя этот элемент может быть привязкой... он не может быть :(. Итак, говоря, что событие всегда будет один, кажется неправильным.
Хорошо, тогда, похоже, стоит использовать защиту типа
Да, конкретно (кажется) для подобных вещей это должен быть охранник типа in
. Настолько разочаровывает, что вам нужно добавлять целые дополнительные операторы if
в действительный/рабочий код только для того, чтобы сообщить TS то, что он должен понять.
Проблема в том, что ваш машинописный код был скомпилирован в код javascript во время выполнения, поэтому у него нет доступа к такой информации, как «какие свойства есть в моем объекте». Он может только статически выводить типы, поэтому «дополнительные if
» необходимы, чтобы сделать тип кода безопасным.
Но иногда переменная может иметь свойство, а может и не иметь его... и это нормально, правда? Мне бы хотелось, чтобы в Typescript был способ сказать: «дайте мне foo.bar
, и если у foo
нет bar
, ничего страшного (я буду использовать undefined
)». Вместо этого вы вынуждены добавлять бессмысленные if
утверждения к «узким» вещам.
Да, в этом сценарии использование foo?.bar
— это нормально, но здесь вы имеете дело с типом, который никогда не имеет нужного вам свойства, поэтому необходима защита типа. В сигнатуре функции вы сообщаете компилятору: «Моя цель имеет этот интерфейс», а в теле функции вы пытаетесь получить доступ к свойствам за пределами интерфейса, который вы сообщили компилятору. Ошибка, с которой вы столкнулись, только что заявила об этом.
Спасибо, это действительно прояснило ситуацию.
• Зависит ли этот вопрос от React? Если да, отметьте это как таковое. Если нет, пожалуйста, отредактируйте свой код так, чтобы это был минимально воспроизводимый пример, которому не нужны внешние библиотеки для демонстрации проблемы. • Я не понимаю, почему замена
e.target.href
наe.target?.href
имеет какое-либо отношение к наличию или отсутствиюhref
. Необязательное связывание меняет то, что происходит, только еслиe.target
сам по себе не определен. Еслиe.target
всегда ненулевое, тоe.target?.href
ничего не меняет. Является ли необязательное связывание отвлекающим маневром в этом вопросе?