user defined type guard 사용기

2yunseong·2023년 5월 11일
0

우아한테크코스 레벨2 미션을 하며 ref를 필요로 하는 이벤트 핸들러가 커져 분리를 하고 싶었다.
가장 먼저 눈에 띈 부분은, 타입 가드 부분을 분리하고 싶었다.

  const increaseQuantity = () => {
    if (
      typeof quantityRef === "function" ||
      !quantityRef ||
      !quantityRef.current
    )
      return;
    const prevValue = +quantityRef.current.value;
    quantityRef.current.value = (prevValue + 1).toString();
  };

생각하기에 타입 가드를 그냥 분리하면 되겠다고 생각하겠지만, 타입 가드를 분리해 메서드로 선언해주면 특수한 작업이 필요하다.
먼저 ref 타입을 살펴보자.

    type ForwardedRef<T> = ((instance: T | null) => void) | MutableRefObject<T | null> | null;

먼저, 미션에서는 상위의 ref를 사용하기 위해 forwardRef를 통해 전달받은 ref의 타입이다.
ForwardedRef는 크게 3가지 타입으로 평가 된다.

  • ((instance: T | null) => void)
  • MutableRefObject<T | null>
  • null

해당 이벤트 핸들러에서 타입을 안전하게 쓰려면 null과 ((instance: T | null) => void)가 되면 안된다. 따라서 다음과 같이 분리해야 한다.

export function isForwardedRef<T>(
  ref: React.ForwardedRef<T>
): ref is MutableRefObject<T> {
  return typeof ref !== "function" && ref !== null;
}

나는 MutableRefObject<HTMLInputElement> 를 원하므로 다음과 같이 분리했다.
typeof ref !== "function"으로 ((instance: T | null) => void)을 거르고
ref !== nullnull을 거를 수 있다.
따라서 이 타입 가드를 거치면 (ref is MutableRefObject<T>) MutableRefObject<T | null>타입을 보장할 수 있다.

이제 MutableRefObject<T | null>로 타입을 한번 걸렀다. 그러나 아직 current는 null일 수 있다는 경고가 뜬다. 그 이유는 앞서 ref는 MutableObject<T | null> 까지만 평가되었기 때문이다.

type definition을 살펴보면 current 는 T 타입으로 정의 되므로 null이 될 수도 있다.

우리는 HTMLInputElement가 ref의 current가 될 것을 알고 있지만, 안전하게 타입 가드를 하나 정의하자.

  export function isRefCurrent<T>(current: T): current is T {
  return current !== null;
}

단순히 null인지만 검사해주면 current의 Type을 보장해줄 수 있다.

그러면 타입 가드 부분을 간단히 만들어 줄 수 있다.

const increaseQuantity = () => {
    if (!isForwardedRef<HTMLInputElement>(quantityRef)) return;
    if (!isRefCurrent<HTMLInputElement>(quantityRef.current)) return;

	// handle uncontrolled components
  };
profile
개발 발자국 남기기

0개의 댓글