다음 글은 Microsoft/TypeScript-React-Conversion-Guide의 예제를 기반으로 작성되었습니다. 본문의 코드는 다음 링크에 있습니다.
본 글은 Javascript에서 Typescript로의 부분적인 전환을 위한 TicTacToc을 예제로 한 가이드이다. 부분적인 전환이 아닌, 새로운 프로젝트를 시작하는 것이라면 다음 링크를 참고하자.
모든 프로젝트에서 Typescript를 채택하는 것은 2단계로 나눌 수 있다.
- 빌드 파이프라인에 TypeScript 컴파일러(tsc)를 추가
- JavaScript 파일을 TypeScript 파일로 변환
프로젝트 전환 전에는 다음과 같은 폴더 구조를 가지고 있다.
TicTacToe_JS /
|---- css/ // css style sheets
|---- src/ // source files
|---- app.jsx // the App React component
|---- board.jsx // the TicTacToe Board React component
|---- constants.js // some shared constants
|---- gameStateBar.jsx // GameStatusBar React component
|---- restartBtn.jsx // RestartBtn React component
|---- .babelrc // a list of babel presets
|---- index.html // web page for our app
|---- package.json // node package configuration file
|---- webpack.config.js // Webpack configuration file
Add TypeScript compiler to build pipeline
Install dependencies
cd TicTacToe_JS
npm install
변환을 위한 각종 컴파일러와 플러그인들을 설치한다. Source-map-loader는 디버깅을 위한 source map을 추가한다. (sourcemap이란?)
npm install --save-dev typescript ts-loader source-map-loader
각각 사용한 라이브러리에 맞게 타입 선언 파일(.d.ts files)을 위한 라이브러리도 다운로드한다.
npm install --save @types/react @types/react-dom
Configure TypeScript
tsconfig.json 파일을 추가해준다.
tsconfig.json 파일의 기본 템플릿은 npx tsc --init 명령어로 생성했으며, 세부 추가 설정 옵션은 아래와 같다.
- 세부 컴파일러 옵션: 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
},
"include": [
"./src/"
]
}
Set up build pipeline
webpack.config.js를 변경해보자.
- . ts 및. tsx파일을 포함할 수 있도록 확장한다.
- babel-loader를 ts-loader로 교체한다.
- 소스 맵 지원을 추가한다.
// webpack.config.js
module.exports = {
// change to .tsx if necessary
entry: './src/app.jsx',
output: {
filename: './bundle.js'
},
resolve: {
// changed from extensions: [".js", ".jsx"]
extensions: [".ts", ".tsx", ".js", ".jsx"]
},
module: {
rules: [
// changed from { test: /\.jsx?$/, use: { loader: 'babel-loader' }, exclude: /node_modules/ },
{ test: /\.(t|j)sx?$/, use: { loader: 'ts-loader' }, exclude: /node_modules/ },
// addition - add source-map support
{ enforce: "pre", test: /\.js$/, exclude: /node_modules/, loader: "source-map-loader" }
]
},
externals: {
"react": "React",
"react-dom": "ReactDOM",
},
// addition - add source-map support
devtool: "source-map"
}
더 이상 필요하지 않은 경우 .babelrc 및 모든 Babel 종속성을 package.json에서 삭제할 수 있다.
추후 타입스크립트로 전환 시, entry: './src/app.jsx',를 entry: './src/app.tsx'로 먼저 전환해주자.
이제 TypeScript로 변환을 처리하는 빌드 파이프 라인이 올바르게 설정되었다. 다음 명령을 사용하여 앱을 번들로 만든 다음 index.html 파일을 브라우저에서 열어보자.
npx webpack
혹시, TypeError: loaderContext.getOptions is not a function가 발생하면, 아래 포스팅을 참고하자.
Transition from JS(X) to TS(X)
이 부분에서는 다음 단계들을 점진적으로 진행한다.
- 하나의 모듈을 TypeScript로 변환하는 최소 단계를 걸친다.
- 더 풍부한 유형 검사를 얻기 위해 하나의 모듈에 유형을 추가한다.
- 전체 코드 베이스에서 TypeScript를 완전히 채택한다.
Minimum transition steps
jsx 파일 중 하나를 tsx로 바꿔보자. 먼저, gameStateBar.jsx를 gameStateBar.tsx로 바꿔보면, VSCode와 같이 Typescript를 지원하는 편집기를 사용하는 경우 몇 가지 빨간색 밑줄이 그어짐을 알 수 있다.
모든 에러를 해결할 여유가 없으면, 우선은 any로 채워 넣고 하나하나씩 바꿔 가면 된다.
마지막 gameState 에러 발생 이유와 해결 방법은 아래와 같다.
최종적으로는 아래와 같이 타입을 지정할 수 있다.
Add types
TypeScript에 제공되는 유형 정보가 많을수록 유형 검사가 더 강력해진다. 구체적인 유형 정보를 추가해보자.
// constants.js
export const playerCell = "X";
export const aiCell = "O";
아래와 같이 구체적인 유형들을 추가적으로 정의해준다.
// constants.ts
export type GameState = "" | "X Wins!" | "O Wins!" | "Draw";
export type CellValue = "" | "X" | "O";
export const playerCell: CellValue = "X";
export const aiCell: CellValue = "O";
앞의 gameStateBar.tsx에서도 해당 유형을 사용해서 gameStateBar component를 선언할 수 있다.
...
import { GameState } from "./constants";
interface GameStateBarState {
gameState: GameState;
}
export class GameStateBar extends React.Component<{}, GameStateBarState> {
...
tsconfig 파일에서 noImplicitAny 옵션으로 암시적 any에 대해 오류를 발생시킬 수 있다.
또한, 액세스 제어를 위해 private/protected를 추가할 수 있다.
private handleGameStateChange(e: CustomEvent) {
this.setState({ gameState: e.detail });
}
private handleRestart(e: Event) {
this.setState({ gameState: "" });
}
Adopt TypeScript in the entire codebase
우리는 위에서 살펴본 Transition from JS(X) to TS(X)의 두 단계를 반복하며, 점진적으로 Javascript를 Typscript로 변환시킬 수 있다. 변환을 하면서 Javascript에서는 필수가 아니지만, Typescript에서는 필수인 부분도 존재한다. 예를 들면, 옵셔널 체이닝이 있다.
모든 변환이 이루어진 폴더는 TicTacToe_TS 링크를 참고하자.
'Tech > Typescript' 카테고리의 다른 글
tsconfig.json을 설정해보자 (0) | 2021.08.19 |
---|---|
Typescript basic template 만들기 (0) | 2021.08.12 |
댓글