TL;DR
- 1年くらいのReactユーザがVue.js + TypeScriptに引っ越しました。
- 当然ながら 小指をぶつけた のでぶつけたポイントを共有します。
- 移住に役立ててください。
このエントリの趣旨/題材
4月の新生活、新しいプログラミング環境にダイブしていく新卒各位お疲れ様です。僕もダイブしています。僕は学生時代React(not TypeScript)ユーザでしたが、業務ではVue.js + TypeScript環境でのコーディングを行っていくことになりました。そこで困ったのが、from Python to Ruby のような移住ガイドが見当たらないことでした。Vue.js 公式ページには Vue.js vs 他のライブラリについては述べられていますが、当然ながらTypeScriptと組み合わせたVue.jsとの比較は言及されていません。
Vue.js + TypeScript は公式サポートも充実していて人口の多い環境だと考えていますので、React からここへやってくる人も多いのではないかなと思います。このエントリはそのような人を対象読者としており、React.js からVue.js + TypeScriptに移住するための手引き になることが期待されています(もちろん一番の目的は僕自身の復習です。死に覚えは基本なので)。
幸いにもたくさんの参考資料があります。題材として、次の記事I created the exact same app in React and Vue. Here are the differencesを取り上げます。この記事の趣旨は同じToDoアプリケーションを2つのフレームワークで作り、両者を比較するというものです。このエントリはそれを Vue.js + TypeScript 環境に移植します。その差異の共有を通じて、なにがしか移住にまつわるポイントを共有できればと思います。
リポジトリは次のURLにあります:
https://github.com/IKKO-Ohta/vue-ts-todoApp

Vue.js / TypeScript の概略 + 組み合わせて使うと何が嬉しいか
まずは前提から確認していきます。Vue.jsならびにTypeScriptについて軽く抑え、組み合わせることによる効用を確認します(すべて公式ページにある内容の抜粋です)。
Vue.js
Vue.jsはUIを構築するためのフレームワークで、Evan Yo(@youyuxi)によって開発されたライブラリです。React, Angularらと比較されることが多く、
- リアクティブな挙動をします /footnote{すなわち、React.Component.setState()のように明示しなくても、stateが変更されるとレンダリングメソッドが走るというのが僕の理解です}
- template-script-styleの3層構造をもっています
- 仮想DOMをもっています。
公式の導入ページがとても参考になります。
TypeScript
TypeScriptは、型付けされた、スーパーセット のJavaScriptです。開発元はMSです。TypeScriptには次のような特色があります:
- 今までのJS Productに親和的で、一部だけTypeScriptに置き換えたり、相互にモジュールを読み込み・書き出しすることができます。
- scaleするJavaScriptとされています。高い生産性をもたらすツールと習慣を取り入れることができます。
- 最新のJSにキャッチアップしているので、ES2015や今後出るES2018にも対応します。
公式の導入ページ 以外でも、https://github.com/Microsoft/TypeScript-React-Starter#typescript-react-starter などが参考になります(Vue, angularなどのスターターセットもあります)。
Vue.js + TypeScript の嬉しさ
潜在的なランタイムエラーを防止できる可能性 があります。また、型情報がついているのでリファクタリングが楽になったり、各種のツールを利用できたりとチーム開発にメリットがあります。Vue プロジェクトが大規模になっていくにつれ、TypeScriptの導入は重要な要素になってくるものと考えられます。
Vueは公式にTypeScriptをサポートしており、そのためのライブラリを配布しています。また、Vue-CLIはTypeScript利用を初期設定として雛形を作ってくれたり、公式 router ライブラリもそれをフォローしていたりと、利用するにあたっては恵まれた環境だとは言えそうです。
小指をぶつけたポイントリスト
以上のような理由で人気になりつつある Vue.js + TypeScript環境ですが、Reactを触った経験値があっても、当然ながら導入にかかるハードルはややあるなというのが感想です。
基本的なシンタックスについての説明は公式ドキュメントの導入に譲るとして、ここでは移行にあたってつまづいたポイントを以下に列挙します。
題材とするのは次の記事とそのリポジトリです:
I created the exact same app in React and Vue. Here are the differences. by @sunilsandhu
https://link.medium.com/RsDm2oXDJT
リポジトリ:
https://github.com/sunil-sandhu/vue-todo
https://github.com/sunil-sandhu/react-todo
日本語訳もあります:
ReactとVueってどう違う?全く同じアプリをReactとVueで作成してみて分かった相違点 https://coliss.com/articles/build-websites/operation/javascript/same-app-in-react-and-vue-here-are-the-differences.html
こちらが僕が作成したVue.js + TypeScript版です。
https://github.com/IKKO-Ohta/vue-ts-todoApp
以下では、特に
https://github.com/IKKO-Ohta/vue-ts-todoApp/blob/master/src/ToDo_ts.vue
のプログラムを取り上げます。
比較として、生のvueで書かれた元リポジトリの該当プログラムは、
https://github.com/sunil-sandhu/vue-todo/blob/master/src/ToDo.vue
です。
objectを型宣言する / インターフェイス定義
22行目付近
interface ATodo {
id: number;
text: string;
}
元プログラムの this.list の要素はobjectであり、listの型宣言は 要素のtype
+[]
なので、何かしらの仕方で要素の型を宣言する必要があります。TypeScriptでは、object[]などという書き方はなく、代わりに interface + []を用います。interfaceは一般的にパスカルケースで表記されます。
なおinterfaceの代わりにanyで受けることもできますが、当然型についての情報がなくなるので望ましい書き方ではないです。
apply, map, filterなど、関数を引数に取る関数の取り扱い
48行目
const newId = Math.max.apply(null, this.list.map((t: ATodo) => t.id)) + 1;
さっそくinterfaceを用いた部分です。アロー関数をTypeScriptで使う際は上のような仕方で書きます。interfaceとしてひとつのtodo ATodo
を定義したおかげで可読性が向上していることが感じ取れる行です。
importのスコープ / @Componentsの設定
27行目
@Component({
components: { ToDoItem }
})
Reactの感覚で見ると小指をぶつけやすいポイントかなと思いました。<template>
以下でimportしたコンポーネント<ToDoItem/>
を宣言していますが、import文を最上層に書くだけではテンプレートに定義されたコンポーネントを正しく解釈することができません。@Component デコレータを用いて、その内部で子コンポーネントを利用することを宣言します。vue側からみると、component プロパティがデコレータに移ったような書き方になります。
これについて有効なドキュメントはないのですが、実際の実装 をみると、確かに@component に渡せることがわかります。
うまくトランスパイルしてくれない / ts-loaderの設定
webpackはvue-cli 3.0に含まれています。ので、TypeScriptなしにつくられたvueプロジェクトにTypeScriptを導入するためには、webpack側にts-loaderを設定する必要があります。
具体的には、vue.config.js ファイルを新しく書くことで対応します。vue-cli 公式ドキュメント に説明があります。
実際には、新しくプロジェクトを切りなおして
$vue create hoge
でセッティングしてしまったほうが早いかもしれませんが。
番外: 拡張子は.vueです
設定次第ではありますが、TypeScriptを使ったからといってファイルの拡張子が即.tsになるということではないです。コンフィグファイルやエディタの設定と合わせて整合的になるように決めればよく、特にVue.js + TypeScriptプロジェクトで 典型的な3層構造をもつコンポーネントは.vueにするべきです。
より詳しくいうと、このような3層構造のファイルは単一ファイルコンポーネントと呼ばれ、単一ファイルコンポーネントはTypeScriptを利用するとしても拡張子は.vueです。
番外: CSS minification error
calc(auto+1px)
はinvalidなcssになっており、初期設定のvue-cli-serviceでビルドするとminificationでコケます。
ERROR Error: CSS minification error: Lexical error on line 1: Unrecognized text.
適用されないだけではなく、指摘されるのにびっくりしました。
Vue.jsに移住したぞ
以上が移住初日にコケたポイントです。もっとたくさんコケる予定なので、そのたびに積極的に死に覚えの学習サイクルを回していけると良いかなと思います。また上のような理由で移住することになったので、Vue.js + TypeScript界隈も積極的にフォローしていく所存です。 何かあれば @samayotta までご指導いただければと思います。
それでは新卒各位プログラミングしていきましょう。僕もプログラミングしていきます。