webpack.config.jsで補完を効くようにする


※vscodeでの開発が前提です

## TL;DR

```js:webpack.config.js
const webpack = require("webpack")

/** @type {Parameters<typeof webpack>[0][0]} */
const config = {
  // ...
}

module.exports = config

```

<img width="606" alt="スクリーンショット 2019-05-09 14.29.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137148/6e1a3d75-7408-8482-55b7-6476dd3b1e08.png">


こんな感じ (jsdocの中身がキモい。。)

## 補足
型が正しく表示されない場合、以下のコマンドを実行してください

```sh
npm i -D @types/webpack
```

vscodeのデフォルトの設定では、package.jsonから自動的に型定義ライブラリをインストールしてくれます。

(@elastic氏のコメントより)

## 解説

[@types/webpack](<https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/webpack>)には以下のように定義されています。(一部略)

```ts
export = webpack;

declare function webpack(
    options: webpack.Configuration,
    handler: webpack.Compiler.Handler
): webpack.Compiler.Watching | webpack.Compiler;
declare function webpack(options?: webpack.Configuration): webpack.Compiler;

declare function webpack(
    options: webpack.Configuration[],
    handler: webpack.MultiCompiler.Handler
): webpack.MultiWatching | webpack.MultiCompiler;
declare function webpack(options: webpack.Configuration[]): webpack.MultiCompiler;

declare namespace webpack {
    interface Configuration {
      	// ...
    }
}

```

`webpack.Configuration`が webpack.config.jsでexportする型です。

tsならば、以下のようにかけます。

```ts
import webpack, { Configuration } from "webpack"

const config: Configuration = {
  // ...
}

export = config
```

~~しかし、`webpack.Configuration`をjs上でimportすることはできません。~~ 追記参照

色々試した結果、`webpack`関数の引数から`Configuration`型の情報を取るという回りくどいやり方でできました。

tsでは、`Parameters`という関数から引数の型の情報を取得する組み込み型があります。

```ts:lib.es5.d.ts
/**
 * Obtain the parameters of a function type in a tuple
 */
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
```

```ts
// 使用例
// Params1, Params2は [number, string] 型になる
type Callback = (a: number, b: string) => void
type Params1 = Parameters<Callback>

function func(a: number, b: string) {}
type Params2 = Parameters<typeof func>
```

`Parameters`を使用し、`webpack`関数から`Configuration`型を取るのをtsで書いてみます。

vscodeにコピペすれば型情報が見れます。

```ts
import webpack from "webpack"

type Webpack = typeof webpack
/*
型 → {
  (options: webpack.Configuration, handler: webpack.Compiler.Handler):
    | webpack.Compiler.Watching
    | webpack.Compiler
  (options?: webpack.Configuration): webpack.Compiler

  (options: webpack.Configuration[], handler: webpack.MultiCompiler.Handler):
    | webpack.MultiWatching
    | webpack.MultiCompiler
  (options: webpack.Configuration[]): webpack.MultiCompiler
  ...
}
*/

type WebpackParameters = Parameters<Webpack>
/*
型→ [webpack.Configuration[]]
*/

type Param = WebpackParameters[0]
/*
型→　webpack.Configuration[]
*/

type Config = Param[number]
/*
型→　webpack.Configuration
*/
```

この型定義をワンランナーにしてさらに短くすると

```ts
import webpack from "webpack"

type Config = Parameters<typeof webpack>[0][0]
```

これをjsdocの`@type`に記述すればokです！

## 余談

webpack環境を一から構築しようとした副産物でこれができたのですが、肝心のwebpackは複雑すぎてまだ使用できていません😭


## 2019/5/10 追記
```js:webpack.config.js
/** @typedef {import('webpack').Configuration} WebpackConfiguration */

/** @type WebpackConfiguration */
const config = {
  // ...
}

```
@elastic氏のコメントより、これでもいけるそうな

こっちの方がわかりやすくていいですね(でもやっぱりjsdocの中身がキモい。。)

また、jsdocの[@typedef](https://devdocs.io/jsdoc/tags-typedef)は初見だったので、知れて良かったです。

@elastic氏、ありがとうございます！

## 2019/5/13 追記2
webpack.config.jsをtsで記述するというやり方を叶えてくれる記事を見つけたのでリンクを貼っておきます。

https://qiita.com/sprout2000/items/cfb2044a75b097b1b5f8

@sprout2000氏、ありがとうございます！