
오늘은 JavaScript 코드로 작성된 Todo List React Project를 TypeScript로 변환해 보았다.
Create React App으로 처음부터 TypeScript 기반 프로젝트를 생성해도 되지만, 실제로 현업에서도 React 버전 업그레이드, 라이브러리 변경, redux → react query 등등 기존 프로젝트를 리팩토링 하는 작업이 많다고 한다.
생각보다 변경할 코드가 많지는 않았지만 에러를 하나하나 해결하는 과정은 굉장히 오래걸렸다. 프로젝트 규모가 커질수록 그 시간은 훨씬 길어지겠지..?
다음번 프로젝트에서 TypeScript를 사용한다면 interface와 enum 등 효율적으로 타입을 쓸 수 있는 방안도 추가로 고민해봐야겠다.
typescript와 기존 react 패키지들을 typescript 버전으로 추가한다.
yarn add typescript @types/node @types/react @types/react-dom
터미널 명령어 tsc --init를 입력해 tsconfig.json 파일을 초기화한다.
// tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/", // path to output directory
"sourceMap": true, // allow sourcemap support
"strictNullChecks": true, // enable strict null checks as a best practice
"module": "es6", // specify module code generation
"jsx": "react", // use typescript to transpile jsx to js
"target": "es5", // specify ECMAScript target version
"allowJs": true, // allow a partial TypeScript and JavaScript codebase
"moduleResolution": "node", // specify module resolution strategy
"allowSyntheticDefaultImports": true
},
"include": ["./src/"]
}
이제부터 js/jsx 파일을 ts/tsx 파일로 하나씩 바꿔나가야 한다.
// index.tsx
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import store from './store/config/configStore';
import { ResetCSS, GradientBackground } from './components/styles/GlobalStyle';
const root = ReactDOM.createRoot(
document.querySelector('#root') as HTMLElement
);
root.render(
<Provider store={store}>
<ResetCSS />
<GradientBackground />
<App />
</Provider>
);
"jsx": "react-jsx" 옵션 추가// AddForm.tsx
<styled.TodoInputErrorMessage visible={visible}>
내용을 입력하세요.
</styled.TodoInputErrorMessage>
// AddForm.style.tsx
// 1) interface 사용하는 방법
interface TodoInputErrorMessageProps {
visible: boolean;
}
// 2) type 사용하는 방법
type TodoInputErrorMessageProps = {
visible: boolean;
};
export const TodoInputErrorMessage = styled.div<TodoInputErrorMessageProps>`
display: ${(props) => (props.visible ? 'block' : 'none')};
position: absolute;
left: 100px;
top: 50px;
color: #e17aa0;
`;
// useInput.ts
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setTodoValue(e.target.value);
};
// useTodoQuery.ts
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const todo = todoValue.trim();
if (!todo) {
setTodoValue('');
setVisible(true);
return;
}
mutationAdd.mutate({ isDone: false, todo });
setVisible(false);
setTodoValue('');
};
// useInput.ts
const mutationDelete = useMutation({
mutationFn: async (id) => {
await axios.delete(`${SERVER_URL}/todos/${id}`);
},
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
});
parameter, return type을 명시한다.//useInput.ts
interface Todo {
isDone: boolean;
todo: string;
}
const mutationAdd = useMutation({
mutationFn: async (newTodo: Todo) => {
await axios.post(`${SERVER_URL}/todos`, newTodo);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
Reference
How to Migrate a React App to TypeScript - SitePoint
GitHub - microsoft/TypeScript-React-Conversion-Guide