// ref가 된 DOM의 type을 generic에 부여한다.
const inputEl = useRef<HTMLInputElment>(null)
// 추론된 타입 👉 React.RefObject<HTMLInputElement>
// 추가적으로 inputEl의 초기값이 null 이기때문에, input의 타입은 아래와 같다.
const input = inputEl.current;
// 추론된 타입 👉 HTMLInputElement | null
// 이 경우 타입의 확실성을 부여 해주기 위해
// if 문안에서 작업을 해주거나, 또는 type assertion (! as ~) 을 해준다.
// if 문 처리 (null 방지)
if(input) {
input.focus()
}
// [ ] 가 초기값인 경우 never로 추론되므로, generic을 통한 명시가 필요하다.
const numbers = useRef<number[]>([]);
// type assertion
const input = inputEl.current as HTMLInputElement;
// 추론된 타입 👉 HTMLInputElement
useRef : 값을 바꾸어도 화면이 re-render가 되지 않는다.
useRef의 Type Overloading
useRef current의 readOnly 이슈
타입스크립트에서 useRef에 대해 3가지 로 타입선언을 해둠 (overloading 이라고 함)
useRef의 initialValue가 null
또는 undefined
일 때 아래의 케이스로 타입이 추론됨
const el_1 = React.useRef<number>(null);
// 추론된 타입 👉 React.RefObject<number>
// RefObject 로 타입이 추론된 경우 current가 readOnly 이므로 수정이 불가하다.
// el_1.current = 1 ❌
// 따라서 initial Value가 null 이고, ref의 current를 수정하는 코드가 필요하다면
// el_2와 같이 generic을 (useRef<T | null>) 선언해줘야 한다.
// T는 사용할 타입을 의미함
const el_2 = React.useRef<number | null>(null);
// 추론된 타입 👉 React.MutableRefObject<number | null>
const el_3 = React.useRef();
// 추론된 타입 👉 React.MutableRefObject<undefined>
// initialValue가 undefined 임에 따라 el_3.current에 들어갈 수 있는 값도 undefined만 가능하다.
// ✨ handler를 useCallback으로 wrapping 하면 'e'가 type을 잃는다. 따라서 아래 방법으로
// typing을 해줘야 한다.
// 방법 1 : event object에 직접 typing
const onChangeHandler = useCallback((e: React.FormEvent)=>{}, [])
// 방법 2 : useCallback의 generic 으로 typing
const onChangeHandler = useCallback<(e: React.FormEvent) => void>((e)=>{}, [])
// generic의 단점 : 가독성이 낮아진다.
// 타입을 generic에 부여한다.
// type 추론이 되기 때문에 언넣어도 상관은 없다.
const [state, useState] = useState<string>("")
const [number, setNumber] = useState<number>(0)
// useState에서 [] 을 initial로 사용하는 경우 never로 추론됨
// 따라서, typing이 필요함. type으로 선언하고, useState의 generic에 삽입
type IAry = {
try: string,
result: string,
}
const [ary, setAry] = useState<IAry[]>([]);
// 추론된 타입 👉 const ary: IAry[]
특별히 typing 할 것이 없다..
// type alias 또는 interface 를 선언 또는 import 해옴
import {TryInfo} from './NumberBaseball'
// 방법 1 : 리액트에서는 보통은 이렇게 안쓴다고 한다
const Try = ({tryInfo} : {tryInfo: TryInfo}) => {
// 중략..
}
// 방법 2 : 이걸 더 많이 써요
const Try: React.FunctionCoponent<TryInfo> ({ TryInfo }) => {
// 중략
}
// 또는 React.FC로 사용도 가능
// React.SFC 는 쓰지 않음
setTimeout(()=>{}, 1000)
// 추론된 타입 👇
function setTimeout<[]>(callback: () => void, ms?: number | undefined): NodeJS.Timeout (+2 overloads)
namespace setTimeout
// 타입이 NodeJS.Timeout 으로 추론된 상태
그래서 setTimeout()
을 사용할 때는 window.setTimeout()
처럼 window
를 명시적으로 사용해준다.
useRef와 useState 어떤 것을 쓰나?
useRef는 값이 변경되어도 re-render가 안되기 때문에 View와 관련된 것이 아니라면 useRef를 쓰고, 화면의 상태와 관련된 것이라면 useState를 쓴다.
as const
값을 고정할 때 사용한다.
const scores = {
r : 1,
s : 0,
p : -1
}
// 이 경우 scores의 타입은 {r: number, s: number, p: number}로 추론된다.
// 하지만 이것은 number 타입이 아니라 상수, 즉 1, 0, -1에서 변하지 않을 값들이기 때문에
const scores = {
// ... 위와 동
} as const
// 와 같이 as const 를 명시해주면, { readonly r: 1; readonly s: 0; readonly p: -1; } 로 타입이 변경된다.
typeof 와 keyof
Object.keys 의 한계
Object.keys(myObj)의 return type은 오직 string[]
이다. 따라서 아래와 같은 케이스에서는 type이 불분명해질 수 있다.
const scores = {
r : 1,
s : 0,
p : -1
} as const
// 추론된 타입 👉 {r: 1, s: 0, p: -1}
Object.keys(scores)
// 추론된 타입 👉 string[]
// 따라서 더 명확한 타입선언을 위해 type assertion을 해준다.
Object.keys(scores) as ['r', 's', 'p']
// 추론된 타입 👉 ["r", "s", "p"]
// 동적으로 assertion 하기위해 typeof 나 keyof를 사용해도 좋다.
const onClickbtn = (params: Type) =>
(event: React.MouseEvent<HTMLButtonElement>) => {
//
}