기존에 JavaScript 기반으로 작성된 React 프로젝트를 TypeScript로 변환하는 과정과 그 과정에서 발생한 에러를 해결하는 과정을 다루도록 하겠습니다.
CRA(Create-react-app)를 사용한 경우와 사용하지 않은 경우로 분리하여 설명하겠습니다.
CRA를 사용하신다면 프로젝트를 TypeScript로 다시 생성하면 됩니다.
npx create-react-app "path" --typescript
그리고 기존 컴포넌트를 새 프로젝트의 src로 옮기고 과정 2를 진행합니다.
yarn add typescript @types/node @types/react @types/react-dom
yarn add -D @babel/preset-typescript ts-loader
module.exports = {
presets: [
'@babel/preset-react',
'@babel/preset-env',
'@babel/preset-typescript',
],
};
{...}
module.exports = {
{...},
entry: './src/index.tsx',
resolve: {
extensions: ['*', '.ts', '.tsx', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{...}
],
}
};
tsc init # TypeScript 기본
npx gts init # Google style
JSX 문법을 포함하는 JS 파일은 tsx, 일반 JS 파일은 ts 확장자로 모두 바꿉니다.
과정을 완료하면 위와 같은 구조가 됩니다. 사진이 너무 길쭉해져 디렉터리를 닫아 놓았을 뿐 하위 디렉터리의 모든 파일도 변경하여야 합니다.
가장 시간이 오래 걸리며 프로젝트 규모에 따라 영겁의 세월이 걸릴 수도 있는 과정입니다.
아래는 TypeScript에서 기본적으로 요구하는 사항 외에 React 또는 연관 라이브러리에 의해 발생하거나 잘 알려진 에러에 대한 해결 방법입니다.
💩 에러가 생기는 코드
const container = document.getElementById('root');
const root = createRoot(container); // error!
React 루트 엘리먼트를 만들 때 container가 null일 수 있어 발생하는 문제입니다.
해당 container가 null일 때에 대해 예외 처리를 해 줍니다.
혹은 타입 단언을 해 주셔도 됩니다.
✅ 수정 결과
const container = document.getElementById('root');
if (!container) throw new Error('Failed to find root element');
const root = createRoot(container);
💩 에러가 생기는 코드
const MyComponent = ({ foo }) => {
return <div>{foo}</div>;
}; // error!
React 함수형 컴포넌트의 인자 타입이 지정되지 않아 발생하는 문제입니다.
type을 지정합니다.
✅ 수정 결과
type MyComponentProps = {
foo?: string;
};
const MyComponent = ({ foo }: MyComponentProps) => {
return <div>{foo}</div>;
}
💩 에러가 생기는 코드
import React from 'react'; // error!
CommonJS 모듈을 ES6 모듈 코드베이스로 가져오려 할 때 발생하는 문제입니다.
Default named import를 다음과 같이 변경합니다.
✅ 수정 결과
import * as React from 'react';
💩 에러가 생기는 코드
const ref = useRef(); // error!
useRef에 맞는 정의가 오버로딩되어 있지 않아 발생하는 문제입니다.
이 정의에 대해 잘 설명해 주신 포스팅이 있습니다.
✅ 수정 결과
const ref = useRef() as React.MutableRefObject<HTMLDivElement>;
💩 에러가 생기는 코드
const StyledAppbar = styled.div`
${(props) =>
props.left &&
css`
{...}
`}
${(props) =>
props.right &&
css`
{...}
`}
`; // error!
props를 사용 중인 경우 해당 프로퍼티에 맞는 인터페이스가 없어 발생하는 문제입니다.
해당 컴포넌트에 맞는 인터페이스를 생성합니다.
✅ 수정 결과
interface AppbarInterface {
left?: boolean;
right?: boolean;
}
const StyledAppbar = styled.div<AppbarInterface>`
${(props) =>
props.left &&
css`
{...}
`}
${(props) =>
props.right &&
css`
{...}
`}
`;
이외에도 적지 않은 TS 에러를 마주칠 수 있습니다. 하지만 그리 걱정하지는 않아도 될 것이, 여러분이 겪었던 에러는 전세계의 누군가는 똑같이 겪었으며 해결 방법도 어딘가에는 올라와 있습니다. 우리의 친구인 Stackoverflow에도요.
부족한 글 읽어 주셔서 감사합니다.