LoginSignup
8
7

More than 3 years have passed since last update.

【webpack5】ts-loader + babel-loader を併用する【TypeScript】

Last updated at Posted at 2021-03-19

ts-loaderとbabel-loaderを組み合わせる

babel-loaderだけで、TypeScriptはトランスパイルが可能です。
具体的には

@babel/preset-typescript

を使います。
もしBabelが必要な状況なのであれば、ts-loaderは必要ありません。
逆に、Babelが不要なのであればts-loaderが必要です。

しかし、

  • レガシーブラウザ向けにBabelを通したもの
  • モダンブラウザ向けにTSをトランスパイルしただけのもの

の両方が必要な場合は、自分はts-loaderbabel-loaderを組み合わせるようにしています。

私の環境ではtsconfig.jsonはいずれの場合も次のようなイメージでした。

ファイル構成
project
│  .babelrc
│  package-lock.json
│  package.json
│  tsconfig.json
│  webpack.config.js
│
├─dist(ここに吐き出す)
│
├─node_modules
│
└─src
   │  hogehoge.ts (具体的な処理を書いていく)
   │  polyfill.ts (polyfillのimportのみ。IE向けのバンドルで統合します。)
tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "newLine": "lf",
    "outDir": "dist"
  },
  "exclude": [
    "node_modules",
    "**/*.test.ts"
  ]
}

IE11のためにTypeScriptをBabelを使ってトランスパイルする構成

package.json
{
  "devDependencies": {
    "@babel/core": "^7.13.10",
    "@babel/preset-env": "^7.13.10",
    "@babel/preset-typescript": "^7.13.0",
    "babel-loader": "^8.2.2",
    "core-js": "^3.9.1",
    "webpack": "^5.26.3",
    "webpack-cli": "^4.5.0"
  }
}
.babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "ie": 11
        },
        "corejs": 3,
        "useBuiltIns": "usage"
      }
    ],
    [
      "@babel/preset-typescript"
    ]
  ],
  "plugins": [
  ]
}
webpack.config.js
const path = require('path');

module.exports = [
  {
    entry: 'hogehoge.ts', // ['polyfill.ts', 'hogehoge.ts'] などのようにしてもよさそう
    output: {
      filename: 'hogehoge.min.legacy.js',
      path: path.join(__dirname, 'dist'),
    },
    resolve: {
      extensions: [
        '.ts',
        '.js',
      ],
    },
    module: {
      rules: {
        test: /\.(ts|js)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
          },
        ],
      },
    },
  },
];

モダンブラウザだけを対象にTypeScriptをts-loaderを使ってトランスパイルする構成

package.json
{
  "devDependencies": {
    "core-js": "^3.9.1",
    "ts-loader": "^8.0.18",
    "tslint": "^6.1.3",
    "tslint-loader": "^3.5.4",
    "typescript": "^4.2.3",
    "webpack": "^5.26.3",
    "webpack-cli": "^4.5.0"
  }
}
webpack.config.js
const path = require('path');

module.exports = [
  {
    entry: 'hogehoge.ts'
    output: {
      filename: 'hogehoge.min.js',
      path: path.join(__dirname, 'dist'),
    },
    resolve: {
      extensions: [
        '.ts',
        '.js',
      ],
    },
    module: {
      rules: {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'ts-loader',
          },
          // linterも通しておくと吉
          // {
          //   loader: 'tslint-loader',
          //   options: {
          //     typeCheck: true,
          //     fix: false,
          //     emitErrors: true,
          //   },
          // },
        ],
      },
    },
  },
];

ts-loaderとbabel-loaderを組み合わせる

  • レガシーブラウザ向けにBabelを通したもの
  • モダンブラウザ向けにTSをトランスパイルしただけのもの

の2種類を出力したい時。

package.json
{
  "devDependencies": {
    "@babel/core": "^7.13.10",
    "@babel/preset-env": "^7.13.10",
    "@babel/preset-typescript": "^7.13.0",
    "babel-loader": "^8.2.2",
    "core-js": "^3.9.1",
    "ts-loader": "^8.0.18",
    "tslint": "^6.1.3",
    "tslint-loader": "^3.5.4",
    "typescript": "^4.2.3",
    "webpack": "^5.26.3",
    "webpack-cli": "^4.5.0"
  }
}
webpack.config.js
const path = require('path');

module.exports = [
  // べた書きだと長いのでそれぞれをモジュール化したり、
  // 重複している部分を変数化したりしてまとめてもよさそう
  {
    // モダン向け
    target: ['web', 'es5'],
    output: {
      entry: './src/hogehoge.ts',
      path: path.join(__dirname, 'dist'),
    },
    filename: 'hogehoge.min.js',
    resolve: {
      extensions: [
        '.ts',
        '.js',
      ],
    },
    module: {
      rules: [
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'ts-loader',
            },
            {
              loader: 'tslint-loader',
              options: {
                typeCheck: true,
                fix: false,
                emitErrors: true,
              },
            },
          ],
        },
      ],
    },
  },
  {
    // IE向け
    entry: [
      './src/_polyfill.ts',
      './src/hogehoge.ts',
    ],
    target: ['web', 'es5'],
    output: {
      filename: 'hogehoge.min.legacy.js',
      path: path.join(__dirname, 'dist'),
    },
    resolve: {
      extensions: [
        '.ts',
        '.js',
      ],
    },
    module: {
      rules: [
        {
          test: /\.(ts|js)$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'babel-loader',
            },
          ],
        },
      ],
    },
  },
];

IE向けとモダンブラウザ向けをそれぞれ読み込む。

JSを読み込ませたいhtmlファイル
<script src="./hogehoge.min.js" type="module"></script>
<script src="./hogehoge.min.legacy.js" nomodule></script>

Module not found: Error: Can't resolve

Babelを使っているときにこういうエラーがでることがあります。

これが出てしまったときは、

  • .babelrcファイル内でcore-jsを3と明示しているか
  • babel-loaderの適応条件(test)が、tsjsを対象としているか(/\.(ts|js)$/
  • resolve.extensionstsjsを指定しているか

Module parse failed: Unexpected token

ts-loader側ではなくbabel-loader側でこれが出てきたら、pluginsの出番です1

さまざまなプラグインが存在しているので、必要なものをnpm i -Dして.babelrcに追記しましょう。

.babelrcの例
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "ie": 11
        },
        "corejs": 3,
        "useBuiltIns": "usage"
      }
    ],
    [
      "@babel/preset-typescript"
    ]
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties"
  ]
}

IE11での「***は定義されていません。」「このオブジェクトではサポートされていない操作です。」など

IEでこれが出てくるときは、core-jsでは賄えないpolyfillの出番です。

この辺とかよく使います。
IE11用のバンドルでしか読み込まないpolyfill.tsでどんどんimportしちゃいましょう(IEで動くように書くよりpolyfill突っ込め派。)。

まとめ

webpackの仕様の深さ、loaderの概念、TypeScript、Babel、polyfill、などなど引っかかるポイントがめちゃめちゃ多くて大変なので、Babelがいらない平和な世界になってほしいと切に願います( ˘ω˘ )

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7