'EventTarget' 형식에 '...' 속성이 없습니다. ts(2339)

1Hoit·2023년 7월 20일

토이프로젝트

목록 보기
7/13
post-thumbnail

태그를 만들고 삭제하는 코드를 작성하던 중 이와 같은 문제가 발생했다.

그 외에도 event.target에 어떤 속성이 없다는 에러가 종종 발생했다.

이럴 때에는 event 파라미터에서 모든 타입을 해결하려고 하지 말고, event와 target을 분리해서 각각의 타입을 지정해주면 된다.

혹은 currentTarget을 이용해서 해결할 수 있었다.

기존 코드

 const deleteTagItem = (e: React.MouseEvent<HTMLButtonElement>) => {
  //여기서 에러 발생
   const deleteTagItem = e.target.parentElement;
   //'EventTarget' 형식에 'parentElement' 속성이 없습니다.ts(2339)

   if (deleteTagItem.firstChild) {
      const dItem = deleteTagItem.firstChild.textContent;
      const filteredTagList = tagList.filter((tagItem) => tagItem !== dItem);
      setTagList(filteredTagList);
    }
  };

해결 1

event와 target을 분리해서 각각의 타입을 지정

const deleteTagItem = (e: React.MouseEvent<HTMLButtonElement>) => {
    
    const target = e.target as HTMLButtonElement;
    const deleteTagItem = target.parentElement
    
    if (deleteTagItem && deleteTagItem.firstChild) {
      const dItem = deleteTagItem.firstChild.textContent;
      const filteredTagList = tagList.filter((tagItem) => tagItem !== dItem);
      setTagList(filteredTagList);
    }
  };

해결 2

currentTarget 사용

const deleteTagItem = (e: React.MouseEvent<HTMLButtonElement>) => {
   
  const deleteTagItem = e.currentTarget.parentElement as Node;
   
  if (deleteTagItem.firstChild) {
      const dItem = deleteTagItem.firstChild.textContent;
      const filteredTagList = tagList.filter((tagItem) => tagItem !== dItem);
      setTagList(filteredTagList);
    }
  };

추가적으로 e.target과 e.currentTarget 을 알아보자

const handleSearchValue = (e: KeyboardEvent<HTMLInputElement>) => {
	setSearchValue(e.currentTarget.value);
	setSearchValue(e.target.value);  // error 발생
};

Property 'value' does not exist on type 'EventTarget'. ts(2339)
위와 같은 에러가 나온다.

왜 그러냐면
e.target과 e.currentTarget의 type alias가 다르기 때문이다

위의 코드를 기준으로 살펴보면

currentTarget: 이벤트 리스너가 바인딩 된 요소

  • currentTarget은 EventTarget에 HTMLInputElement type을 확장한 형태이다.

target: 이벤트가 발생한 바로 그 요소

  • currentTarget과 달리 EventTarget에 HTMLInputElement를 확장하지 않았다.

  • HTMLInputElement를 확장하지 않은 이유는 EventTarget이 될 수 있는 요소들에는 HTML Element 말고 다른 요소들도 있기 때문이라고 한다.

    ⇒XMLHttpRequest, FileReader, AudioNode, AudioContext 등등

그래서 해결책은?

1. target 대신 currentTarget 사용

currentTarget과 target이 동일할 경우는 currentTarget을 사용하자.

const handleSearchValue = (e: KeyboardEvent<HTMLInputElement>) => {
	// 1. currentTarget과 target이 동일할 경우
	setSearchValue(e.currentTarget.value);
 
	// 2. n개의 DOM 구조일 경우 -> currentTarget 기준으로 value 찾기
	const target = e.currentTarget;
 
	const findId = target.querySelector('span').id
	setSearchValue(findId);
		
	const findValue = target.querySelector(span).value
	setSearchValue(findValue);
};

불가피하게 target을 사용해야 되는 경우가 많다. 그럴 때는 currentTarget을 기준으로 DOM을 탐색해야 한다. 이 경우 currentTarget 밑으로 자식이 n개인 상태에서 자식들의 구조가 복잡한 상황에서 특정 자식을 찾는 경우 findChild(’childName’) 식으로 순차적으로 [자식 → 자식의 자식 → … ] 순으로 재귀 탐색을 해야 해서 비용이 큰 작업이 돼서 부담이 될 수 있기 때문에 아래와 같은 방법을 사용할 수도 있다.

2. 타입 단언(type assertion)

const handleSearchValue = (e: KeyboardEvent<HTMLInputElement>) => {
	setSearchValue((e.target as HTMLInputElement).value);
};

보통 타입 단언을 지양하지만, target이 되는 DOM이 문서에서 명확하게 정해져 있다면 이 경우는 타입 단언하는 방식을 사용해도 괜찮을 것 같다고 한다.

추가적으로 ChangeEvent의 경우

e: ChangeEvent<HTMLInputElement>로 타입을 줄 경우
ChangeEvent는 EventTarget을 확장하고 있어 해당 에러가 발생하지 않는다.


참고

참고사이트

profile
프론트엔드 개발자를 꿈꾸는 원호잇!

1개의 댓글

comment-user-thumbnail
2023년 7월 20일

좋은 글 감사합니다!

답글 달기