props로 전달하는 onClick 함수의 타입 지정하기

이나리·2022년 8월 31일
10

간단하게 지정한 콜백함수 타입의 문제점

버튼 컴포넌트를 재사용 가능한 컴포넌트로 만들기 위해, 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

마지막에는, 결국 오류 구문에 나타난 타입과 동일하게 추론된다는 것을 확인할 수 있습니다.

0개의 댓글