타입스크립트를 조각조각 접하다보니 모르는 부분이 많아, 전반적으로 이해하고 싶어 개인 프로젝트에 TS를 적용해보았습니다..!
React, webpack, babel로 구성된 작은 개인 프로젝트에 적용하였고, 과정을 이 포스트에 단계별로 정리했습니다. 100% 타입스크립트 짱짱 적용..! 까지는 아니고, 일단 마이그레이션에 필수적으로 필요한 정도의 내용이 담겨있습니다. 비슷한 작업을 앞둔 분들에게 도움이 되었으면 좋겠네요. ☘️
커밋과 함께 보시려면 이 PR을 확인해주세요. -> https://github.com/hon9g/Draggable/pull/3
우선 크게 보자면 다음과 같은 과정을 거치게됩니다.
npm i -D typescript @types/node @types/react @types/react-dom @babel/preset-typescript
tsc cli 커맨드 사용을 위해,
brew install typescript
타입스크립트 설정 파일 tsconfig.json 생성을 위해,
tsc --init
(저의 경우) 프로그램에 포함하고 싶은 파일이 root 기준으로 ./ src/
에 위치하고 있으므로, tsconfig에 "include": ["./src/"],
을 추가합니다.
(tsconfig.json 가 위치하는 곳이 타입스크립트 Root로 취급 됨.)
.tsx, .ts 파일에 대한 rule을 추가해줍니다.
webpack.config.js
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
use: ['babel-loader'],
exclude: /node_modules/,
},
...
],
},
preset에 타입스크립트를 추가해줍니다.
.babelrc
{
"presets": [
...,
"@babel/preset-typescript"
]
}
"noImplicitAny": false
"jsx": "preserve"
로 주었습니다. (TS: JSX config)true
로 바꾸었습니다. 그러고나면 타입 추론이 되지 않아, 암묵적으로 any 처리된 곳은 컴파일 에러가 발생합니다. 그런 부분들을 찾아 적절한 타입을 지정해줍니다.ref
& React.useRef
타입 지정div객체의 optional한 인자인 ref는 LegacyRef<T>
타입의 객체를 받습니다.
type Ref<T> = RefCallback<T> | RefObject<T> | null;
type LegacyRef<T> = string | Ref<T>;
useRef는 전달하는 인자가 있는지 없는지, 또 null
값인지 아닌지에 따라 다른 타입을 리턴값을 돌려줍니다.
undefined
를 넘기는 경우 -> voidnull
값을 넘기는 경우 -> RefObject<T>
null
값이 아닌 초기 값을 넘기는 경우 -> MutableRefObject<T>
따라서 LegacyRef<T>
타입에 해당하지 않는 객체를 div ref에 전달해주면 에러가 발생합니다.
다시말해, useRef
를 초기화 할 때, 인자를 넘기지 않는다면 void한 함수가 넘어가기 때문에 에러가 발생합니다.
이 에러를 해결하기 위해서는 아래와 같이 null
값으로 초기화를 해주면됩니다.
또한 리턴 타입인 RefObject<T>
은 제네릭을 가지고 있으므로 적절한 타입을 넘겨주어야합니다.
const ref = React.useRef<HTMLDivElement>(null)
...
return (
<div ref={ref} />
)
(삽질 노트 더보기: useRef 타입지정 어떻게하지?)
React EventHandler에서 꼭 타입을 지정해줘야하는 부분은 EventHandler 함수의 인자 부분입니다.
이 인자들은 @types/react
패키지에서 React namespace 하위에 지정된 event 타입에 해당합니다.
적절한 event 타입을 지정해주면, 해당 함수가 그 이벤트에 대한 EventHandler 인 것으로 추론됩니다.
이 때, 제너릭으로 타겟이되는 element의 타입을 제너릭으로 넘겨주어야 합니다.
React.MouseEvent<T>
-> 핸들러 합수 타입: MouseEventHandler<T>
React.TouchEvent<T>
-> TouchEventHandler<T>
(삽질 노트 더보기: React 이벤트 핸들러 타입은 어떻게 지정해줘야하지?)
window객체에 직접 이벤트리스너를 추가할 때, 이벤트 핸들러 함수의 타입 또한 지정해야합니다.
마찬가지로 인자들의 타입을 지정하는게 필수적이고, 인자는 event 타입에 해당합니다.
이 인자들은 @types/react
패키지에 정의되어 있습니다. 그런데 React namespace 하위의 이벤트 타입이 아니라 글로벌하게 정의되어있는 이벤트 타입을 사용해야합니다.
☘️☘️☘️☘️☘️☘️☘️☘️🍀 끝 🍀☘️☘️☘️☘️☘️☘️☘️☘️