frontend/개발환경

babel은 ts를 어떻게 해석할까?

장구루애비 2021. 12. 13. 10:53

babel이 typescript를 어떻게 해석하는지 찾아보기 위해 babeljs.io 에서 @babel/plugin-transform-typescript를 검색해봤다.

 

@babel/plugin-transform-typescript

@babel/plugin-transform-typescript · Babel

 

Babel · The compiler for next generation JavaScript

The compiler for next generation JavaScript

babeljs.io

이 페이지에서 얻은 주요 힌트가 몇 가지 있다.

  1. 이 플러그인은 @babel/preset-typescript 프리셋에 포함 되어 있다.
  2. 이 플러그인은 타입 유형을 해석하기 위한 것이다. 타입스크립트가 제공하는?.,??,this.#x등의 문법까지 지원하려면 @babel/preset-env 를 함께 사용해야 한다.

 

📖 ?., ??, this.#x 이러한 문법은 각각 optional chainning, nullish coalescing, private fields (class properties) 의 문법으로 불린다.

 

첫 번째 힌트를 따라 @babel/preset-typescript 에 가서 무슨 차이점이 있나 살펴봤다.

 

@babel/preset-typescript

@babel/preset-typescript · Babel

 

Babel · The compiler for next generation JavaScript

The compiler for next generation JavaScript

babeljs.io

여기서는 이 프리셋이 @babel/plugin-transform-typescript 플러그인을 포함하고 있다는 내용 말곤 별다른 힌트를 얻지 못했다.

크게 중요한 것 같진 않지만 다음 인용문 하나를 확인할 수 있었다.

 

You will need to specify --extensions ".ts" for @babel/cli & @babel/node cli's to handle .ts files.
번역기: @babel/cli및@babel/node cli가.ts파일을 처리하려면--extensions ".ts"를 지정해야 합니다.

 

의식의 흐름을 따라가보니 CRA(create react app)에서는 어떻게 typescript를 지원할까? 에 도달했다.

 

CRA 분석

CRA를 통해서 타입스크립트를 지원하는 리액트 프로젝트를 만들어봤다.

# ts로 시작하는 react 프로젝트 생성
yarn create react-app cra-ts --template typescript
# 다음 명령으로도 프로젝트 생성 가능
# npx create-react-app cra-ts --template typescript

# 방금 생성한 프로젝트로 이동
cd cra-ts

 

CRA프로젝트는 react-scripts의 버전에 따라서 빌드 관련 설정이 감춰져 있는데, eject 명령을 통해 감춰진 설정을 추출할 수 있다.

config/ 디렉터리에서 각종 설정파일들을 확인할 수 있다.

yarn eject

 

CRA는 webpack 번들러를 이용하여 소스코드를 빌드하거나 개발용 서버를 실행하기 때문에 config/webpack.config.js 파일을 열어 typescript를 키워드로 검색해봤고, babel-loader 설정에 달려있는 주석문을 통해 하나의 힌트를 얻었다.

 

The preset includes JSX, Flow, TypeScript, and some ESnext features.
번역기: preset에는 JSX, Flow, TypeScript 및 일부 ESnext 기능이 포함됩니다.

 

찾았다! preset에 typescript를 포함한다는데 어떤 preset인가 보니 babel-preset-react-app 이었다.

아직 의문점이 남긴 하지만 CRA는 babel-preset-react-app을 통해 typescript를 지원한다!는 것을 알 수 있었다.

 

config/webpack.config.js

// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
  test: /\.(js|mjs|jsx|ts|tsx)$/,
  include: paths.appSrc,
  loader: require.resolve('babel-loader'),
  options: {
    customize: require.resolve(
      'babel-preset-react-app/webpack-overrides'
    ),
    presets: [
      [
        require.resolve('babel-preset-react-app'),
        {
          runtime: hasJsxRuntime ? 'automatic' : 'classic',
        },
      ],
    ],
    // ...other options
  },
},

 

📖 webpack은 각종 .js, .ts, .svg, .css, .scss 등의 자원들을 -loader 라는 도구를 이용하여 웹브라우저가 해석 가능한 html, css, js 파일로 번들링 해주는 도구이다.

 

내가 처음에 생각했던 @babel/plugin-transform-typescript 플러그인은 보이지 않았기에 내 의식은 babel-preset-react-app 을 찾아가기 시작했다.

 

babel-preset-react-app

create-react-app/packages/babel-preset-react-app at main · facebook/create-react-app

 

GitHub - facebook/create-react-app: Set up a modern web app by running one command.

Set up a modern web app by running one command. Contribute to facebook/create-react-app development by creating an account on GitHub.

github.com

여기서 내가 원하는 답에 매우 근접한 문구를 찾았다.

 

Make sure you have a tsconfig.json file at the root directory. You can also use the typescript option on .babelrc
번역기: 루트 디렉토리에 tsconfig.json 파일이 있는지 확인하십시오. .babelrc에서 typescript 옵션을 사용할 수도 있습니다.

 

루트 디렉터리에 tsconfig.json이 있으면 typescript를 지원하는구나!

또는 babelrc 설정을 아래처럼 구성하면 .ts를 .js로 transpile 할 수 있구나!

{
  "presets": [["react-app", { "flow": false, "typescript": true }]]
}

 

여기까지 와보니 예전에 ts 기반의 리액트 컴포넌트를 패키지로 배포하기 위해서 @babel/preset-react-app과 @babel/preset-typescript를 함께 사용한 적이 있는데. 이건 프리셋을 중복해서 등록한 것과 같다는 것을 깨달았다... 나중에 꼭 고쳐야지.

  • babel 설정 개선!

 

이제 거의 다 왔기 때문에 babel-preset-react-app의 소스코드를 좀 찾아봤다.

이 preset을 구성하는 presets 설정을 보니 @babel/preset-env@babel/preset-react 그리고, typescript 여부에 따라 @babel/preset-typescript 프리셋이 추가되는 것을 알 수 있었다.

 

create.js

module.exports = function (api, opts, env) {
    // ... 
    return {
        presets: [
          // ...
          (isEnvProduction || isEnvDevelopment) && [
            // Latest stable ECMAScript features
            require('@babel/preset-env').default,
            {
              // Allow importing core-js in entrypoint and use browserlist to select polyfills
              useBuiltIns: 'entry',
              // Set the corejs version we are using to avoid warnings in console
              corejs: 3,
              // Exclude transforms that make all code slower
              exclude: ['transform-typeof-symbol'],
            },
          ],
          [
            require('@babel/preset-react').default,
            {
              // Adds component stack to warning messages
              // Adds __self attribute to JSX which React will use for some warnings
              development: isEnvDevelopment || isEnvTest,
              // Will use the native built-in instead of trying to polyfill
              // behavior for any plugins that require one.
              ...(opts.runtime !== 'automatic' ? { useBuiltIns: true } : {}),
              runtime: opts.runtime || 'classic',
            },
          ],
          isTypeScriptEnabled && [require('@babel/preset-typescript').default],
        ].filter(Boolean),
        plugins: [
                // ...
            ],
    }
}