[TypeScript] e.target.target 타입 에러가 나는 이유 ts(2339)
1. 문제 발생
const handleSearchValue = (e: KeyboardEvent<HTMLInputElement>) => {
setSearchValue(e.currentTarget.value);
setSearchValue(e.target.value); // error 발생
};
e.target과 e.currentTarget의 type alias가 다르다구..? 외 안되?!
react/index.d.ts 를 까보면
// @types/react/index.d.ts
interface BaseSyntheticEvent<E = object, C = any, T = any> {
nativeEvent: E;
currentTarget: C;
target: T;
// ...
}
- KeyboardEvent는 BaseSyntheticEvent 타입을 extends하고 있다.
2. currentTarget와 target
1-1. currentTarget: 이벤트 리스너가 바인딩 된 요소
e.currentTarget의 interface를 까보자.
// @types/react/index.d.ts
interface BaseSyntheticEvent<E = object, C = any, T = any> {
nativeEvent: E;
currentTarget: C;
target: T;
// ...
}
// e.currentTarget 타입
(property) BaseSyntheticEvent<
globalThis.KeyboardEvent,
EventTarget & HTMLInputElement,
EventTarget
>.currentTarget: EventTarget & HTMLInputElement
- currentTarget은 EventTarget에 HTMLInputElement type을 확장한 형태
1-2. target: 이벤트가 발생한 바로 그 요소
이번에는 e.target의 interface를 까보자.
// e.target 타입
(property) BaseSyntheticEvent<
globalThis.KeyboardEvent,
EventTarget & HTMLInputElement,
EventTarget
>.target: EventTarget
- currentTarget과 달리 EventTarget에 HTMLInputElement를 확장하지 않았음!
왜 e.target type은 HTMLInputElement를 확장하지 않았을까?
3. e.target type은 HTMLInputElement를 확장하지 않은 이유
HTMLInputElement를 확장하지 않은 이유는 EventTarget이 될 수 있는 요소들에는 HTML Element 말고 다른 요소들도 있기 때문임
⇒XMLHttpRequest, FileReader, AudioNode, AudioContext 등등
2-1. Event target이 될 수 있는 다른 타입들
- FileReader 인터페이스
- XMLHttpRequest 인터페이스
- AudioNode 인터페이스
- BaseAudioContext 인터페이스
4. 해결책
4-1. 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);
};
currentTarget과 target이 동일할 경우는 currentTarget을 사용하자.
하지만 불가피하게 target을 사용해야 되는 경우가 많다. 그럴 때는 currentTarget을 기준으로 DOM을 탐색해야 한다. 이 경우 currentTarget 밑으로 자식이 n개인 상태에서 자식들의 구조가 복잡한 상황에서 특정 자식을 찾는 경우 findChild(’childName’) 식으로 순차적으로 [자식 → 자식의 자식 → … ] 순으로 재귀 탐색을 해야 해서 비용이 큰 작업이 돼서 부담이 될 수 있기 때문에 이럴 경우는 다음 해결책을 추천
4-2. 타입 단언(type assertion)
const handleSearchValue = (e: KeyboardEvent<HTMLInputElement>) => {
setSearchValue((e.target as HTMLInputElement).value);
};
보통 타입 단언을 지양하지만, target이 되는 DOM이 문서에서 명확하게 정해져 있다면 이 경우는 타입 단언하는 방식을 사용해도 괜찮을 것 같음.
4-3. ChangeEvent 사용
// e: ChangeEvent<HTMLInputElement>로 타입을 줄 경우
// ChangeEvent는 EventTarget을 확장하고 있어 해당 에러가 발생하지 않음
// React/index.d.ts
interface ChangeEvent<T = Element> extends SyntheticEvent<T> {
target: EventTarget & T;
}
const handleSearchValue = (e: ChangeEvent<HTMLInputElement>) => {
setSearchValue(e.currentTarget.value);
}
참고: ChangeEvent, KeyboardEvent 각각 interface 비교
'프로그래밍 > Typescript' 카테고리의 다른 글
[이펙티브 타입스크립트] 객체 래퍼 타입 피하기 (0) | 2022.06.10 |
---|---|
[이펙티브 타입스크립트] 타입 공간과 값 공간의 심벌 구분하기 (0) | 2022.06.05 |
[이펙티브 타입스크립트] #4 구조적 타이핑에 익숙해지기 (0) | 2022.06.05 |
댓글
이 글 공유하기
다른 글
-
[이펙티브 타입스크립트] 객체 래퍼 타입 피하기
[이펙티브 타입스크립트] 객체 래퍼 타입 피하기
2022.06.10 -
[이펙티브 타입스크립트] 타입 공간과 값 공간의 심벌 구분하기
[이펙티브 타입스크립트] 타입 공간과 값 공간의 심벌 구분하기
2022.06.05 -
[이펙티브 타입스크립트] #4 구조적 타이핑에 익숙해지기
[이펙티브 타입스크립트] #4 구조적 타이핑에 익숙해지기
2022.06.05