본문 바로가기
Tech/Typescript

Partial migration of javascript to typescript

by egas 2021. 10. 7.
다음 글은 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 명령어로 생성했으며, 세부 추가 설정 옵션은 아래와 같다.

{
    "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가 발생하면, 아래 포스팅을 참고하자.

https://egas.tistory.com/138

 

[Typescript] TypeError: loaderContext.getOptions is not a function

jsx를 tsx로 마이그레이션 하기 위해 ts-loader와 Webpack을 설정했다. (repo-url) npx webpack Webpack을 실행하자 다음과 같은 에러가 났다. TicTacToe_TS_hochan % npx webpack Hash: a8dde2af1ae05d91a224 Ve..

egas.tistory.com

 

Transition from JS(X) to TS(X)

이 부분에서는 다음 단계들을 점진적으로 진행한다.

  • 하나의 모듈을 TypeScript로 변환하는 최소 단계를 걸친다.
  • 더 풍부한 유형 검사를 얻기 위해 하나의 모듈에 유형을 추가한다.
  • 전체 코드 베이스에서 TypeScript를 완전히 채택한다.

Minimum transition steps

jsx 파일 중 하나를 tsx로 바꿔보자. 먼저, gameStateBar.jsx를 gameStateBar.tsx로 바꿔보면, VSCode와 같이 Typescript를 지원하는 편집기를 사용하는 경우 몇 가지 빨간색 밑줄이 그어짐을 알 수 있다.

모든 에러를 해결할 여유가 없으면, 우선은 any로 채워 넣고 하나하나씩 바꿔 가면 된다.
마지막 gameState 에러 발생 이유와 해결 방법은 아래와 같다.

https://egas.tistory.com/140

 

[Typescript] TS2339: Property 'gameState' does not exist on type 'Readonly<{}>'.

javascript에서 typescript로 마이그레이션 중 다음 에러가 발생했다. 문제의 코드는 아래와 같다. import React from "react"; export class GameStateBar extends React.Component { constructor(props: any) {..

egas.tistory.com

최종적으로는 아래와 같이 타입을 지정할 수 있다.

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 링크를 참고하자.

728x90

'Tech > Typescript' 카테고리의 다른 글

tsconfig.json을 설정해보자  (0) 2021.08.19
Typescript basic template 만들기  (0) 2021.08.12

댓글