Vue.jsをTypeScriptで書く環境を構築する (Sublime Text編)

スラマッパギ！この記事は [Retty Inc. Advent Calendar 2017](https://qiita.com/advent-calendar/2017/retty) 19日目です。
昨日は @makoto-nagai さんによる [Core Imageを使ったiOS上での画像フィルタの実装](https://qiita.com/makoto-nagai/items/21cb119d036b7db346af) でした。

今日はフロントエンド開発者をいつも悩ませる開発環境ネタです。

# TL;DR
Language Server Protocolを使う
 
# Vue.js :heart:  TypeScript
去る2017年10月前後、TS + Vue界隈に激震が走ったことは皆様も記憶に新しいものだと思います。

そうです。:tada: [Vue 2.5 release](https://medium.com/the-vue-point/vue-2-5-released-14bd65bf030b) :tada: の **「Better TypeScript Integration」** です。
これまでTS + Vueを書く際、オブジェクトリテラルのメソッド内でthisの型推論がうまく行われないため、jsの書式とは一風変わった以下のような `vue-class-component` を利用したクラスベースのシンタックスで書かざるを得ませんでした。

```ClassBase.ts
// https://github.com/vuejs/vue-class-component/blob/master/README.md より引用
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
  props: {
    propMessage: String
  }
})
export default class App extends Vue {
  // initial data
  msg = 123

  // use prop values for initial data
  helloMsg = 'Hello, ' + this.propMessage

  // lifecycle hook
  mounted () {
    this.greet()
  }

  // computed
  get computedMsg () {
    return 'computed ' + this.msg
  }

  // method
  greet () {
    alert('greeting: ' + this.msg)
  }
}
```

対して下記が上記のコードを通常のJSのシンタックスに直したものです。

```ObjectLiteral.js
export default {
  props: {
    propMessage: String,
  },
  data() {
    return {
      msg: 123,
    };
  },
  computed: {
    computedMsg: {
      get() {
        return 'computed' + this.msg;
      },
    },
  },
  methods {
    greet() {
      alert('greeting: ' + this.msg);
    },
  },
};
```

上の比較でもわかるように、クラスベースのシンタックスではJSのオブジェクトリテラルのシンタックスとは大きく異なったAPIを利用する必要がありました。
自分はプライベートではTSで書くことが多いので、TSで初学をしようとしましたが、JSのドキュメントを読みながらだと、全くインターフェースが違う書き方にいつまでも慣れず、諦めてしまいました :innocent: 

そんな中、TypeScriptチームの[やっていきなPR](https://github.com/Microsoft/TypeScript/pull/14141)でthisの型推論問題が解決されたことをきっかけに、Vue 2.5のリリースへの第一歩が踏み出されました[^1]。

Vue 2.5以降では

```ObjectLiteral.ts
import Vue from 'vue';

export default Vue.extend({
  props: {
    propMessage: String,
  },
  data() {
    return {
      msg: 123,
    };
  },
  computed: {
    computedMsg: {
      get() {
        return 'computed' + this.msg;
      },
    },
  },
  methods {
    greet() {
      alert('greeting: ' + this.msg);
    },
  },
});
```

のように `Vue.extend` の引数にオブジェクトを入れる以外違いのない形で書けるようになっています。
コントリビューターの皆さんありがとうございます！！ :pray_tone1: :pray_tone2: :pray_tone3: :pray_tone4: :pray_tone5:  

# We :heart: Sublime Text
Sublime Text をお使いの皆様、お待たせしました。
[TypeScript-Sublime-Plugin](https://github.com/Microsoft/TypeScript-Sublime-Plugin) が入ったSublime Textで`.vue` ファイルを書こうと思い、Type Hintingが効かずに :innocent: な気持ちになった方も少なからずいらっしゃるのではないでしょうか。

以前まではエディタで正しく補完をさせるには下記のように `.vue` ファイルを分割するのがデファクトでした。
[Separating the html, css, and js into separate files](https://github.com/vuejs/vueify/issues/35)

```Component.vue
<template src="./template.pug"></template>
<scripts src="./scripts.ts"></scripts>
<style src="./style.stylus"></style>
```

ですが、Single File Component の利点である関心のスコープとファイルスコープが一致する点を捨ててまでこんなことしたくない！！

ということでLaungage Server Protocolの導入でこれを解決します。

## Language Server Protocolとは

Language Server Protocolは2016年6月にMicrosoftが公開した、エディタ・IDEが開発支援機能だけに分離されたサーバとJSON-PRCで通信する際のプロトコルです。

詳細は [Microsoft/language-server-protocol](https://github.com/Microsoft/language-server-protocol) や [language server protocolについて (前編)](https://qiita.com/atsushieno/items/ce31df9bd88e98eec5c4) を参照してください。

今回利用したいVueのSingle File Componentの開発支援をするLanguage Serverは、VSCodeのVue.js開発支援ツール[vetur](https://github.com/vuejs/vetur) の中の [/server](https://github.com/vuejs/vetur/tree/master/server) としてリポジトリがホスティングされています。
また、これは `vue-language-server` として[npmパッケージ](https://www.npmjs.com/package/vue-language-server)が登録されています。

以下手順です。

## 手順
### 1. Package Control で LSP を入れる
以下 [tomv564/LSP](https://github.com/tomv564/LSP) を利用して話を進めます。

### 2. vue-language-server を入れる

`npm install -g` が許せるタイプの人であれば

```sh
$ npm install -g vue-language-server
```

をしてしまっていいと思います。

自分は許せないので

```sh
$ cd path/to/repository/
$ npm install -D vue-language-server
```

して、npm scriptsに以下のように追記します。

```package.json
{
  "scripts": {
    "vls": "vls"
  }
}
```

どちらでもよいですが、

```sh
$ vls # or npm run vls

> vue-ts-template@1.0.0 lsp /Users/xuesuli/src/vue-ts-template
> vls
```

となればOKです。

### 3. SublimeでLSPの設定をする
ドキュメントは [Sublime LSP Plugin Documentation](https://github.com/tomv564/LSP/blob/master/docs/index.md) を確認してください。

```js
// LSP.sublime-settings -- User
{
    "clients": {
        "vue": {
            "command": ["vls"], // or ["npm", "run", "vls"]
            "scopes": ["text.html.vue"],
            "syntaxes": ["Packages/Vue Syntax Highlight/vue.tmLanguage"],
            "languageId": "vue"
        }
    }
}
```

で完了です。

# :tada: 
はかどる
<img width="483" alt="スクリーンショット 2017-12-18 13.08.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/16562/90d5ff53-30dd-5a90-f2e9-0ec1425ebf81.png">


明日は @shinichi-nakagawa-r さんの `REST API提供者と自分にやさしいAPIクライアントをPythonでいい感じに作る方法` です。

また明日！スラマッマラム！

[^1]: 詳細は https://medium.com/the-vue-point/upcoming-typescript-changes-in-vue-2-5-e9bd7e2ecf08