문제 해결/리팩터링

TypeScript 리팩터링 - useRef 타입 오버로딩 파악하고 사용하기

imKion 2023. 3. 23. 18:24

❗️ 문제

로그인, 회원가입 폼에 애니메이션을 트리거하기 위해서 만든 useRef에 타입을 줘야 했다.

본질적으로 useRef는 .current 프로퍼티에 변경 가능한 값을 담고 있는 “상자”와 같다.

처음엔 이런 형태로 타입을 부여했었다.

const inputRef = useRef<HTMLInputElement>(null);

그런데 인풋의 ref 속성으로 부여된 useRef 값의 current에 접근할 수 없는 문제가 생겨서 그 이유를 살펴보았다.

 

🔎 원인

useRef 훅은 3개의 정의가 오버로딩되어 있다.

 

1. useRef<T>(initialValue: T): MutableRefObject<T>;

인자의 타입과 제네릭의 타입이 T로 일치하는 경우, MutableRefObject<T>를 반환한다.

MutableRefObject<T>의 경우, 이름에서도 볼 수 있고 위의 정의에서도 확인할 수 있듯 current 프로퍼티 그 자체를 직접 변경할 수 있다.

 

2. useRef<T>(initialValue: T|null): RefObject<T>;

인자의 타입이 null을 허용하는 경우, RefObject<T>를 반환한다.

RefObject<T>는 위에서 보았듯 current 프로퍼티를 직접 수정할 수 없다.

 

3. useRef<T = undefined>(): MutableRefObject<T | undefined>;

제네릭의 타입이 undefined인 경우(타입을 제공하지 않은 경우), MutableRefObject<T | undefined>를 반환한다.

 

정의로 보면 다음과 같이 생겼다.

interface MutableRefObject<T> {
    current: T;
}

interface RefObject<T> {
    readonly current: T | null;
}

중요한 건 MutableRefObject은 current가 수정 가능하지만 RefObject는 readonly가 붙어있어서 불가능하는 것이다. 즉 초기값으로 null을 보냈기 때문에 current에 접근이 안 됐었다.

 

🎉 해결

간단히 3번 오버로드를 사용하는 것으로 해결했다.

const inputRef = useRef<HTMLInputElement>();

초기값을 비워서 undefined로 타입을 부여하면 뮤터블 하게 사용이 가능했다.