Google Apps Scriptのコード補完を徹底解説 #gasja

Google Apps Script（以降はGASと記載）の辛いポイントのひとつがスクリプトエディタ上のコード補完(コンテンツアシスト)です。
「スクリプトエディタのコード補完は、効かないときもあるし、型が全然あってないときもあるし、どうすればいいんだ（泣）」
という方はかなり居ると思います。（筆者もそうでした）

本記事の目的は、補完ルールについて理解し「なるほど、そういうことだったのか。」と思ってもらえるように書きました。
補完ルールをマスターしてGASをより楽しく便利に利用していきましょう。

※[コード補完についてのドキュメント](https://developers.google.com/apps-script/guides/services/
)はあるのですが、このページを見てもまったくわかりません。


前回はログ設計について記事を書いたのでこちらもよかったらどうぞ。
[Google Apps Scriptのログ設計 #gasja - Qiita](http://qiita.com/howdy39/items/4dc11af7b1c74c17f7f7)


# ルール1 変数へ代入された型は補完される
一番基本となる形です。
変数に代入された型を推測して補完してくれます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/87d8ea72-ebd1-7ba2-91bc-2f55f15e679b.png)


# ルール２ コメントアウトされていても補完される
コメントアウトされていようが関係なく補完されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/f8f59eb9-6655-85f5-ebfc-c1168e90ba32.png)

## コメントアウトされても補完される！？
コメントアウトされてるのに補完って何故？と思うかもしれませんが、有効活用できるシーンがあります。
例えば、以下のように配列の中身や、関数の引数で渡ってきたパラメータは通常、補完されません。

### 配列の中身までは考慮されない
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/977e2316-4993-4d5e-d77c-03b8bdec462c.png)

### 関数の引数も考慮されない
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/ffd5e605-0bfe-ccd6-a161-119d0f24d7a6.png)

このような場合でもコメントで型を教えてあげることで補完が可能になります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/ae62af71-bbdf-6344-fe98-e32ccb4fb240.png)


# ルール3 ルール1・ルール2の補足
ルール1・ルール２にはさらに細かいルールがあります。
それを解説していきます。

以下のコードを例に、各々の変数がどのように補完されるか、または補完されないのかを解説してきます。


```javascript
   var events1  = CalendarApp.getEvents(new Date(), new Date())[0];
   var events2 = CalendarApp.getEvents(new Date(), new Date())[0]; // コメント
   var events3 = CalendarApp.getEvents(new Date())[0];
// var events4 = CalendarApp.getEvents(new Date())[0];
// var events5 = CalendarApp.getEvents()[];
// var events6 = CalendarApp.getEvents;
// var events7 = CalendarApp.getEvents[;
// var events8 = CalendarApp.getEvents[];
// var events9 = CalendarApp.getEvents[]; // コメント
//     events10 = CalendarApp.getEvents[]
```

### events1
```javascript
CalendarApp.getEvents(new Date(), new Date())[0];
```

CalendarEvent型で補完されます。

[CalendarApp.getEvents(startTime, endTime)](https://developers.google.com/apps-script/reference/calendar/calendar-app#getEvents(Date,Date))
は予定情報を取得するメソッドで、返す型は[CalendarEvent](https://developers.google.com/apps-script/reference/calendar/calendar-event)型の配列です。
配列の最初の値を取りだしているので、CalendarEvent型で補完されます。


### events2
```javascript
var events2 = CalendarApp.getEvents(new Date(), new Date())[0]; // コメント
```

これは補完されません。
コメント文がコードの後にあると何故か補完されません。


### events3
```javascript
var events3 = CalendarApp.getEvents(new Date())[0];
```

CalendarEvent型で補完されます。

実はこれを実行するとエラーになります。
getEventsメソッドに1つの引数のメソッドは存在しないのです。
つまり、補完の考慮時に引数の数は見ていないということです。[^1]
[^1]: 同じメソッド名で引数の数が違うというのはないということですかね。（普通はそうですが）


### events4
```javascript
// var events4 = CalendarApp.getEvents(new Date())[0];
```

CalendarEvent型で補完されます。

コメントアウトされているかは関係ありません。


### events5
```javascript
// var events5 = CalendarApp.getEvents()[];
```

CalendarEvent型で補完されます。

メソッドの引数に加えて、配列の添字の有無も見ていません。


### events6
```javascript
// var events6 = CalendarApp.getEvents;
```

CalendarEvent[]型で補完されます。

メソッドの引数どころか丸括弧すら見ていないのです。


### events7
```javascript
// var events7 = CalendarApp.getEvents[;
```

CalendarEvent型で補完されます。

配列の中身になるかどうかは角括弧の始まりしか見ていません。


### events8
```javascript
// var events8 = CalendarApp.getEvents[];
```

CalendarEvent型で補完されます。

これも若干気持ち悪いですが丸括弧をみていないので補完されます。


### events9
```javascript
// var events9 = CalendarApp.getEvents[]; // コメント
```

補完されません。

コメントアウト内でも文の後にコメントがあると補完対象外になります。


### events10
```javascript
//     events10 = CalendarApp.getEvents[]
```

CalendarEvent型で補完されます。

`var`や末尾の`;`は補完には無関係です。


## コメントで補完したい場合はどう書くのがよいのか
好みの問題ですが、筆者はevents5の引数や添字を抜いた書き方が好きです。

```javascript
// var events = CalendarApp.getEvents();
// var event = CalendarApp.getEvents()[];
```

## 補完を呼ぶ側について
以下のようにメソッドの引数や配列の添字はなくても補完されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/5ff421b9-d11a-481e-ef90-2a2a6d474fe6.png)

メソッドの引数や、配列の添字は見ていません。
添字の引数が`i`や`j`になっても問題なく補完されます。



# ルール4 スコープの概念はない
その変数がアクセス可能かどうかなんてのは見ていません。
ファイルに対して、変数にはどんな型が入っているかをみています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/d1979c80-98e8-9da6-4ba4-44f2a1c23bcf.png)


# ルール5 型は上書きされる
配列で宣言した後に同じ変数名でCalendarEvent型の配列で上書きしています。
CalendarEvent型で認識されているのがわかります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/295086f7-2dec-7440-5253-4c437d1f1174.png)


# ルール6 JSDocを書いても補完されない
以下の場合、`arrayGlobal`は補完されません。
`arrayGlobal2`は補完されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/97133f8b-3070-7a7b-8ca0-ebc257276dbe.png)


# ルール7 関数は補完されない
関数は補完されません。JSDocを書いてもダメです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/98242d99-f56e-4987-c0c4-9b4deaecfe63.png)


# ルール8 別ファイルに書いた変数は補完されない
GASではひとつのプロジェクトに複数のファイルを含めることができます。
グローバルに書いた変数や関数は共有されていますが補完はされません。（実行時は単純にファイルがくっついているイメージ）


# ルール9 ライブラリの場合
ライブラリを参照したときの補完ルールは今までと完全に別ロジックだと思ってよいです。
ルール6,7,8を破って関数なんかもきちんと補完させることができます。

ライブラリってなんだ？という方は以下の記事がおすすめです。
[GASのライブラリを使って楽したい③ 自分でライブラリを作ってみる(:3っ)っ -=三[布団]﻿ - Qiita](http://qiita.com/soundTricker/items/5f542faf5f2a06bc3091)

以下のコードを用いて解説します。

```javascript:ライブラリのコード
// 補完に表示されるが、型はObjectとして表示される
var libVar = [];

// コメントは補完に表示されない
//var libVar2 = [];

/**
 * Objectの配列として宣言
 * JSDoc(@type)を書くと型が表示される
 * @type {Object[]}
 */
var libVar3;

/**
 * 関数
 * 関数は補完に表示される。
 * @param {CalendarApp.CalendarEvent[]} events 配列
 * @return {CalendarApp.CalendarEvent} 文字列の戻り値
 */
function libFunction() {
}

/**
 * 関数(_が末尾にある)
 * _(アンダースコア)関数は補完に表示されない。
 * ※そもそも呼び出しが出来ない、いわゆるprivate扱いになる。
 * @return {string} 文字列の戻り値
 */
function libFunction_() {
}
```

### コードではなくJSDocが補完の元ネタになる

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/a8e86956-4604-2569-c188-748011aa443d.png)

`libVar3`なんかは右辺がなくてもJSDocだけで認識されていますし、コメントアウトされている`libVar2`は出てきません。

関数libFunctionも実際は引数や戻り値がないですが補完されます。
実際の定義ではなくJSDocをもとに補完されているのがわかるかとおもいます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/ba189a62-4e53-9994-8e29-1477df30f31b.png)



# その他

## 補完は完璧ではない

### Array型
例えばArray型の補完を見てみると以下のようなメソッドが補完対象となります。
※見づらいので領域はcss書き換えて拡げています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/673b691d-eeda-d810-4a7e-5d5ecb224183.png)

しかしこれは明らかに少ないので[^2]、ビルドインオブジェクトに関しては補完してくれるものもあるぐらいの認識の方がよさそうです。
[^2]: forEachとかがない


# 参考

- [Google Apps Scriptの開発手法まとめ - Qiita](http://qiita.com/soundTricker/items/4d04c97c499b22886dfd)
- [GASのScriptEditorオレオレチートシート - Qiita](http://qiita.com/soundTricker/items/c9091a63de5401f2d49e)
- [GASのライブラリを使って楽したい③ 自分でライブラリを作ってみる(:3っ)っ -=三[布団]﻿ - Qiita](http://qiita.com/soundTricker/items/5f542faf5f2a06bc3091)