버튼 컴포넌트를 재사용 가능한 컴포넌트로 만들기 위해, onClick 함수를 props로 받도록 만들었는데, 이것을 타입스크립트와 함께 사용하려면 타입도 지정해줘야 합니다.
원래는 버튼 요소에 전달된 onClick 함수의 타입은 에디터를 통해 쉽게 확인할 수 있는데, 그 방법을 알기 전이었기 때문에 일단은 타입 오류만 피하고자, 콜백 형태로 간단하게만 지정했습니다.
interface Props {
onClick(): void;
}
하지만 버튼 컴포넌트를 사용하는 입장에서 onClick 함수를 사용할 때, 매개변수를 전달한다던지, 이벤트 객체를 사용하려고 할 수도 있습니다.
function Button({ onClick }: Props) {
return (
<button onClick={onClick}>버튼</button>
);
}
function App() {
function onClick(e: React.MouseEvent) {}
return (
<Button onClick={onClick} />
);
}
그런데 이렇게 되면, 전달받는 onClick 함수의 타입은 매개변수에 대한 타입을 별도로 지정하지 않았기 때문에, 타입 오류가 발생합니다.
아래는 발생한 오류 구문입니다. 친절하게 어디가 문제인지 잘 설명해주고 있죠.
Type '(e: MouseEvent<Element, MouseEvent>) => void' is not assignable to type '() => void'.
위의 오류 구문에서 나타난 타입을 가지고 지정해줘도 되지만, 리액트 타입 문서에 사용하기 더 좋은 타입의 형태가 있을 지도 모릅니다.
다음과 같이 마우스 이벤트 핸들러 타입이 지정되어 있는 것을 찾을 수 있습니다.
type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
interface Props {
onClick: React.MouseEventHandler<HTMLButtonElement>;
}
이 타입을 지정하게 되면, 앞서 오류 구문에 나타난 (e: MouseEvent<Element, MouseEvent>) => void
과 동일한 타입을 나타낼 수 있습니다.
버튼 컴포넌트에 사용할 것이니, Element 자리에만 HTMLButtonElement 타입으로 조금 더 구체화합니다.
이제 매개변수를 사용한 함수가 전달되더라도 오류가 발생하지 않고, 타입이 호환됩니다.
문제는 해결했으니, 왜 그렇게 되는지 타입을 한번 분석해볼까요?
분석을 위해, MouseEventHandler
타입과 관련된 타입을 모두 가져왔습니다. 눈으로만 보지 말고 하나씩 대입해보면, 어렵지 않게 타입을 추론할 수 있습니다.
말로 풀어 설명하지 않고, 주석을 통해 타입을 대입하는 과정을 나열해보겠습니다.
interface Props {
onClick: React.MouseEventHandler<HTMLButtonElement>;
}
type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
// React.MouseEventHandler<HTMLButtonElement>
// = EventHandler<MouseEvent<HTMLButtonElement>>;
interface MouseEvent<T = Element, E = NativeMouseEvent> extends UIEvent<T, E> {}
// React.MouseEventHandler<HTMLButtonElement>
// = EventHandler<MouseEvent<HTMLButtonElement, NativeMouseEvent>>;
type EventHandler<E extends SyntheticEvent<any>> =
{ bivarianceHack(event: E): void }["bivarianceHack"];
// React.MouseEventHandler<HTMLButtonElement>
// = EventHandler<MouseEvent<HTMLButtonElement, NativeMouseEvent>>
// = { bivarianceHack(event: MouseEvent<HTMLButtonElement, NativeMouseEvent>): void }["bivarianceHack"];
// = (event: MouseEvent<HTMLButtonElement, NativeMouseEvent>): void
// onClick 함수의 타입 추론 결과
onClick(event: MouseEvent<HTMLButtonElement, NativeMouseEvent>): void
// type NativeMouseEvent = MouseEvent; 이므로 아래와 같이 추론됩니다.
onClick(event: MouseEvent<HTMLButtonElement, MouseEvent>): void
마지막에는, 결국 오류 구문에 나타난 타입과 동일하게 추론된다는 것을 확인할 수 있습니다.