Visual Studio Codeで依存モジュールの型定義を使いたい(JavascriptのNode.jsで)

本記事は[cloudpack あら便利カレンダー 2019](https://benri2019.cloudpack.jp/)の記事です

タイトルが難しい。何をしたいのか伝えられた自信がない

以前 [VS CodeでJavascriptの型判定を行う - Qiita](https://qiita.com/dbgso/items/137237a0d14503bc3daa) という記事を書きました。

この記事では触れていなかったのですが、これどうやるんだろう？っていう内容がありました。

# やりたいこと

* 依存モジュールの`type`や`interface`で定義された情報をローカル変数の型に指定したい
* 指定した型に基づいてVisual Studio Codeに補完してほしい
* Node.jsで

mongoclient触っている時に思ったので、これを例にします

クエリを投げる時にどんなクエリが投げられるか忘れるので、型補完してほしい。
そのために`FilterQuery`という`type`が用意されているのでJavascriptでもそれを利用したい。

```javascript
// こんな感じで指定したら
/** @type {FilterQuery} */
const query = {
    // ここで色々補完してほしい
}
```

# 補完してみる

## Typescriptなら簡単

普通にimportして型を指定すれば良いです。

```typescript
import { FilterQuery, MongoClient } from "mongodb";


interface User {
    name: string
}

const client = new MongoClient("");
const query: FilterQuery<User> = {
}
```

こうしておけば、`query`の中身を編集する際にキャプチャのように候補を出してくれます。

![alt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330514/7cac72ea-e935-f5a7-a5a1-b83a9791491a.png)

これと同じことをJavascriptでしたい

## Javascriptの場合

色々試してみました

### requireで読み込み

Javascriptの場合は`require`で読み込めるので、以下でいけるかなと思ったらダメでした。

```javascript
const mongo = require('mongodb')

/** @type {mongo.FilterQuery} */
const query = {}
```

requireで読み込んだ場合は`FilterQuery`が存在しない(参照できない)状態でした。
`const client = new mongo.MongoClient("");`は見えるんですけどね
Javascriptでは本来存在しないので`export type ***`や`export interface ***`は見えないようにしている？

というわけで、`require`では無理でした。

### importで読み込み

JavascriptのES6を使えば同様に`import`できるので、これを使えば補完はできます。

```javascript
import { FilterQuery, MongoClient } from "mongodb";
/** @type {FilterQuery} */
const query = {}
```

ただし、動かせません(意味なし)。
Node.jsではCommonJSで動作するので、ECMA Scriptのモジュールシステムは使えないです。

```
(function (exports, require, module, __filename, __dirname) { import { FilterQuery } from "mongodb";
                                                                     ^
SyntaxError: Unexpected token {
```

動かすためには`.mjs`という拡張子にする必要があるようです。([Node.js(v8.5.0以降)でBabelやwebpackを利用せずにimport/exportを利用する - Qiita](https://qiita.com/Kaoru_Yamamoto/items/bcca9b63220e23b69399))

今回はこの方法はやめておきます


### JSDocにimportを埋め込む(成功)

じゃあどうするか、ということになるんですが、JSDocの`type`の中に`import`を埋め込めば補完対象としてくれました。

コードは以下です。

```javascript
//@ts-check
const mongo = require('mongodb')
class User {
    constructor() {
        /** @type {string} */
        this.name = undefined;
    }
}
const client = new mongo.MongoClient("");
/** @type {import("mongodb").FilterQuery<User>} */
const query = {
    name: {
        
    }
};
```

ちゃんと補完も効きました

![alt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330514/99e0c27a-3af2-0074-a52c-8f2643e13054.png)

一応目標達成。ちょっと面倒ですが

# 問題点

補完を効くようにする、という意味では成功だったのですが、問題点がありました。
jsdocコマンドでJSDocを生成しようとするとエラーを吐きました。

```
ERROR: Unable to parse a tag's type expression for source file type.js in line 10 with tag title "type" and text "{import("mongodb").FilterQuery<User>}": Invalid type expression "import("mongodb").FilterQuery<User>": Expected "!", "=", "?", "[]", "|", or end of input but "(" found.
```

ここにimportが来ることなんて想定してないよ、とのことです。残念

まだ調べきれてないのですが、なんとか設定とかで回避できないですかね？
JSDocあまり詳しくはないので、詳しい方いたら教えてください。


# まとめ

Javascriptで型補完を支援する方法を書きました。
真面目に書く時はTypescriptを使うようになったので、どうしても必要というほどではなかったんですが、もうちょっとというところだったので色々試してみました。

Javascriptメインだという方は使ってみてください。
で、もっといい方法があったら教えてください

# 参考

[VS CodeでJavascriptの型判定を行う - Qiita](https://qiita.com/dbgso/items/137237a0d14503bc3daa)
[Node.js(v8.5.0以降)でBabelやwebpackを利用せずにimport/exportを利用する - Qiita](https://qiita.com/Kaoru_Yamamoto/items/bcca9b63220e23b69399)