다음과 같이 열고 닫음을 처리하는 커스텀 훅이 존재한다고 가정하자.
import { useState, useCallback } from 'react'
const useToggle = (initialState: boolean) => {
const [state, setState] = useState(initialState)
const toggle = useCallback(() => {
setState((state) => !state)
}, [])
const open = useCallback(() => setState(true), [])
const close = useCallback(() => setState(false), [])
return [state, open, close, toggle]
}
export default useToggle
위와 같은 코드에서 [state, open, close, toggle]이 추론되는 걸로는 (boolean | (() => void))[] 과 같이 추론된다.
즉, 저 배열이 boolean 혹은 () => void 꼴의 함수를 원소로 가진 지닌 Array로 인식하는 것이다.
따라서 저 커스텀 훅을 사용하는 곳에서 아래와 같이 타이핑이 들어간다.
const [showDocuments, , , ] = useToggle(hasCurrentDocument
<OtherLibraryProject
showDocuments={showDocuments} // boolean이 아닌 boolean | (() => void) 라고 추론됨
...props
/>
이를 해결하기 위해서는 튜플을 사용하면 된다.
튜플은 Array와 다르게 길이를 알고 있고 각 요소의 유형들을 알고 있어 의도한대로 타입 추론이 되므로 튜플로 반환해주면 된다.
가장 빠른 건 const assertion이다.
const useToggle = (initialState: boolean) => {
// ...omitted
return [state, open, close, toggle] as const
}
이제 이렇게 올바르게 추론된다. readonly [boolean, () => void, () => void, () => void]
as const가 아닌 직접 타이핑을 줌으로써 튜플을 반환하고 싶다면 그렇게 해도 된다.
type useToggleReturnType = [boolean, () => void, () => void, () => void]
const useToggle = (initialState: boolean): useToggleReturnType => {
// ...omitted
return [state, open, close, toggle]
}
Tuple
https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types
as const를 왜 씀?
tuple을 정의하기 위해서는 1) tuple typing을 해주거나 2) const assertion을 하면 된다.
as const에 대해서 조금 살펴보자면, javascript에서 배열이나 객체는 const로 선언하더라도 내부 값을 변환시킬 수 있었으나, as const로 const assertion을 하게 되면 readonly가 붙어 내부 변환을 할 수 있다. 즉 정말로 말그대로 '상수'처럼 취급되는 것이다.
const a = [1, 2, 3];
a[2] = 100;
const b = [1, 2, 3] as const; // readonly [1, 2, 3]
b[2] = 100; // Cannot assign to '2' because it is a read-only property.
좀 더 자세한 내용은 아래를 참고하자.
https://darrengwon.tistory.com/1130?category=867626
'React, Next, Redux > 🚀 React with Hooks' 카테고리의 다른 글
custom hook (4) : intersection observer를 활용한 useScrollFadeIn hook (0) | 2021.01.24 |
---|---|
custom hook (3) : scrollIntoView hook (0) | 2021.01.23 |
custom hook (2) : setInterval wrapping하기 (0) | 2021.01.21 |
custom hook (1) : Detect click outside of DOM (0) | 2021.01.21 |
useState의 지연 초기화 (Lazy initial state) (0) | 2020.10.29 |