型情報有りで設定(webpack.config.js)ファイルを書く

VSCode での作業を想定してます。

JSDoc コメントの@type@typedefでは TypeScript の型ファイルを参照できます。これらのアノテーションを用いて値に型を付けると、例えばmodeの値を入力する時にdevelopmentproductionnoneがサジェストされるようになります。

webpack 型をインストール

webpackwebpack-cliはインストール済なら、もう1つ@types/webpackもインストールします。

yarn add -D @types/webpack

これで型定義部分でimport('webpack')のように書くと@types/webpackが提供する型を参照できるようになりました。

型を付ける

単に設定オブジェクト全体に型を付けたい場合はConfiguratiionを使います。ここで言う設定オブジェクトはoutputentrymoduleなど全てを持ったオブジェクトの事です。

オブジェクト

これは以下のように設定します。

/**
 * @type {import('webpack').Configuration}
 */
module.exports = {
  /* 設定の中身 */
};

これだけでmodeなどにサジェストされるようになったはずです。
Webpack の設定には配列で複数の設定オブジェクトを置いたり、関数で設定オブジェクトを置いたりといくつか別の形態を持ちます。

配列

配列の場合はconfigurationの後ろに[]を置くだけです。

/**
 * @type {import('webpack').Configuration[]}
 */
module.exports = {
  /* 設定の中身 */
};

関数

関数の場合は@typeではなく@returnsを使います。これにより戻り値の型を付けれます。

/**
 * @returns {import('webpack').Configuration}
 */
module.exports = () => ({
  mode: 'development'
});

ちなみにこの関数は、良くenvargvと名付けられる引数2つを受け取れますが、これらに型を付けたり場合は以下のようにします。

/**
 * @param {Object} env
 * @param {boolean} env.production
 * @param {Object} argv
 * @param {import('webpack').Configuration['mode']} argv.mode
 * @returns {import('webpack').Configuration}
 */
module.exports = (env, argv) => ({
  mode: env.production ? 'production' : 'development'
  // mode: argv.mode
  /* ... */
});

env{production: boolean}なオブジェクトで、argv{mode: string}のようなオブジェクトと型を付けしました。これでプロパティにアクセスする時にproductionmodeなどがサジェストされます。

プロミス

戻り値には設定オブジェクトを解決するプロミスでも良いので、その場合はこんな感じに。

/**
 * @returns {Promise.<import('webpack').Configuration>}
 */
module.exports = () =>
  new Promise(resolve => {
    resolve(/* ... */);
  });

共通設定を切り出す

共通設定を切り出すということは設定オブジェクトConfigurationから幾つかのプロパティを取り出して、必要なプロパティだけの新たなオブジェクト型を定義する必要があります。
当たらなオブジェクト型を定義するには@typedefアノテーションを使います。

例えばこのアノテーションは以下のようにすると{value: string}な型のオブジェクトをFooという名前で定義できます。Fooを使うときには、これまでと同じく@typeで使います。

/**
 * @typedef {Object} Foo
 * @property {string} Foo.value
 */

/**
 * @type {Foo}
 */
const foo = {value: 'something'};

設定オブジェクトが配列な時、modeというのは実行時に渡した環境変数などで決める内容なので切り分けると効率が良さそうです。それには{mode: ...}という内容の共通オブジェクトCommonWeConfigurationを定義して、適当な変数にその型を付けてあげます。

/**
 * @typedef {Object} CommonWebpackConfiguration
 * @property {import('webpack').Configuration['mode']} CommonWebpackConfiguration.mode
 */

/**
 * @type {CommonWebpackConfiguration}
 */
const common = {
  mode:
    process.env.NODE_ENV === "production"
      ? "production"
      : "development"
};

Configurationinterfaceですが、ここから更に各プロパティへアクセスするには['property']にインデックスにアクセスするような形で書きます。