Cloud Functions を TypeScript で書く

iOS や Android で Firebase を使っているなら、 [Cloud Functions for Firebase](https://firebase.google.com/docs/functions/?hl=ja) を書く時にも型が欲しくなりますよね。
というわけで Cloud Functions を TypeScript でかける環境を構築してみましょう。

※ 今回構築した環境は [starhoshi/TypeScript\-Firebase\-Cloud\-Functions\-Sample](https://github.com/starhoshi/TypeScript-Firebase-Cloud-Functions-Sample) に push してあります。

## 前提条件

* Firebase Project が作成済みであること
* JavaScript で Cloud Functions の Hello World が動作済みであること
    * [はじめに: 最初の関数の記述とデプロイ  \|  Firebase](https://firebase.google.com/docs/functions/get-started) の通りにやるだけです

## TypeScript の導入

前提として JavaScript で Cloud Functions が動いているはずなので、そこに TypeScript を導入していきましょう。

### npm install typescript 

`npm i typescript` だけで typescript が install できます。ついでに tslint も入れておきましょう。

```bash
$ cd functions
$ npm i typescript tslint --save-dev
```

### tsconfig.json

同階層に tsconfig.json というファイルを作リます、どうコンパイルするかなどの設定です。
以下は [tsconfig\.json · TypeScript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) を参考にちょっと書き換えたものです。

```json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "./",
    "noImplicitAny": true,
    "strictNullChecks": true,
    "sourceMap": true
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modeles"
  ]
}
```

`tsc --init` でも生成できますがイマイチだったのでコピって持ってきた方が楽な気がします。

### package.json

package.json の `"scripts"` に build, watch, deploy の3行を追加しましょう、コンパイルやデプロイなどのコマンドです。

```ts
{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "serve": "firebase serve --only functions",
    "shell": "firebase experimental:functions:shell",
    "start": "npm run shell",
    "logs": "firebase functions:log",
    "build": "tsc", // typescript compile
    "watch": "tsc --watch", // ファイルを監視し自動 compile
    "deploy": "tsc && firebase deploy --only functions" // compile & deploy
  },
  ...
}
```

これで TypeScript の環境ができました :tada:

### index.ts

では実際に typescript で Cloud Functions を書いていきましょう。
`functions/src/` に `index.ts` を作成してください。

```ts
import * as functions from 'firebase-functions'

export const helloWorld = functions.https.onRequest((request, response) => {
  response.send("Hello from Firebase!\n\n")
})
```

次に以下のコマンドでコンパイルします。

```
$ npm run build
```

そうすると `functions/index.js` が生成されているはずです。

```js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const functions = require("firebase-functions");
exports.helloWorld = functions.https.onRequest((request, response) => {
    response.send("Hello from Firebase!\n\n");
});
//# sourceMappingURL=index.js.map
```

問題なく TypeScript を JavaScript に変換できました :tada:

ちなみに `npm run watch` とするとファイルを監視して自動コンパイルしてくれます。

### deploy

ではこの関数をサーバで動かしてみましょう。

```bash
$ npm run deploy
...
Function URL (helloWorld2): https://hoge.cloudfunctions.net/helloWorld

✔  Deploy complete!
```

と出るはずです。この url を curl で叩いてみましょう。

```bash
$ curl https://hoge.cloudfunctions.net/helloWorld
Hello from Firebase!
```

と出ましたね、無事 Deploy が成功しました、これで TypeScript で開発していけます。 


## tslint

環境構築は終わったのですがせっかくなので Lint を導入しましょう。

### tslint.json

```bash
$ ./node_modules/tslint/bin/tslint --init
```

と打つと以下の json が生成されます。

```json
{
    "defaultSeverity": "error",
    "extends": [
        "tslint:recommended"
    ],
    "jsRules": {},
    "rules": {},
    "rulesDirectory": []
}
```

[TSLint core rules](https://palantir.github.io/tslint/rules/) にルールの説明があるので必要に応じて on/off していきましょう。
今回は標準に従っていくことにします。

### エディタ

エディタはなんでもいいのですが、 [Visual Studio Code](https://code.visualstudio.com/) が色々気が利いていて楽です。
[TSLint](https://marketplace.visualstudio.com/items?itemName=eg2.tslint) の拡張を install しましょう。

そうすると `[tslint] ' should be " (quotemark)` `[tslint] Missing semicolon (semicolon)` というエラーがエディタに表示されるので、以下のようにコードを修正しましょう。

```typescript
import * as functions from "firebase-functions";

export const helloWorld = functions.https.onRequest((request, response) => {
  response.send("Hello from Firebase!\n\n");
});
```

`"` を `'` にしてセミコロンを追加しました。これで tslint のエラーも解決できました。

## おわり

これで lint チェックしつつ TypeScript を書いていけるようになりました :muscle:
型のある言語で開発できるとモデル定義などもできて良いですね、やっていきましょう。

## 参考

* [starhoshi/TypeScript\-Firebase\-Cloud\-Functions\-Sample](https://github.com/starhoshi/TypeScript-Firebase-Cloud-Functions-Sample)
* [functions\-samples/typescript\-getting\-started at master · firebase/functions\-samples](https://github.com/firebase/functions-samples/tree/master/typescript-getting-started)