こんにちは。
印刷のラクスルでフロントエンドを担当している菅野です。
現在、稼動中のとあるプロダクトへのTypeScript導入を進めています。今回は、既存プロダクトへの影響を最小限に留めつつTypeScriptを導入する手順をご紹介します。
TypeScriptとは
TypeScript - JavaScript that scales.
TypeScriptは、Microsoftが開発したオープンソースのプログラミング言語です。
詳細な説明は省きますが、以下のような特徴があります。
- JavaScriptの厳密なスーパーセット(≒上位互換)
- 省略可能な型システム
- クラスベースのオブジェクト指向
TypeScript導入にあたって
今回TypeScriptを導入することで、以下のようなメリットがあります。
- 型システムの恩恵が得られる
- エディタの入力補完を受けられる
- コード=ドキュメントという状況を作りやすい
型システムの恩恵が得られる
TypeScriptを導入する最大の理由です。
APIレスポンスの定義をDBに近づけることで、実行時エラーの危険性を大幅に減らすことが出来ます。また、ReduxやVuexで複雑になりがちな状態管理にも型を導入するメリットは大きいです。
コンパイルエラーを無くす=ランタイムエラーを無くすということを最大の目標としています。
エディタの入力補完を受けられる
こちらも大きなメリットとなります。
定義した変数はもちろん、Objectのプロパティ等も型込みでエディタの補完を受けられるようになるため、開発効率がアップします。
コード=ドキュメントという状況を作りやすい
コメント含め、コードに関するドキュメントは、書くのもメンテナンスをするのも大変です。
TypeScriptで型をしっかり書いていけば、引数の型や戻り値の型に関するドキュメントは必要なくなります。「コードが仕様を体現する」という状況を作りやすくなるわけです。
本記事のゴール
- 既存プロダクトにおいて、TypeScriptで記述できるようにすること
.tsファイル及び.vueファイルでの<script lang="ts">を有効にする
- 既存ロジックには影響をあたえないこと
- 型検査は出来るようにすること
TypeScript導入の下準備
本手順はmizchiさんのGistを参考にさせていただいています。
導入対象プロダクトのフロントエンド構成について
JavaScript周りは以下の構成となっています。
- Vue.js 2.5.17
- npm 6.4.1
- webpack 4.35.2
typescript, ts-loaderのinstall
webpackでバンドルするため、typescript本体に加えてts-loaderをinstallします。
npm install typescript ts-loader
導入時点でのバージョンは以下の通りです。
- typescript 3.5.3
- ts-loader 6.0.4
※ --save-devではない理由としては、フロントエンドモジュールのインストール及び本番ビルドをデプロイサーバー上で行っているためです。
webpack.config.jsにts-loaderの設定を追加
const config = {
...,
module: {
rules: [
...,
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true, /* コンパイルのみで型検査を行わない */
appendTsSuffixTo: [/\.vue$/] /* .vueファイルをTSとして読み込むようにする */
}
}
]
},
]
}
}
今回のポイントはtranspileOnly: trueです。このオプションを指定することにより、ts-loaderは型検査を行わなくなります。
tsconfig.jsonの追加
プロジェクトのrootにtsconfig.jsonを追加します。
このファイルはtypescript compiler(tsc)に関する設定を記述するファイルです。
基本構成はVue.js公式ドキュメントを参考にしています。
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"sourceMap": true,
"importHelpers": true,
"esModuleInterop": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,/* import Vue from 'vue';のような形式を有効にする */
"noImplicitThis": true, /* thisの型推論を有効にする */
"baseUrl": "./",
"paths": {
"@/*": [
"src/javascripts/*"/* '@/account/register'のようにimportできるようにする */
]
}
},
"include": [
"./src/javascripts/**/*.ts",
"./src/javascripts/**/*.tsx",
"./src/javascripts/**/*.vue"
],
"exclude": [
"node_modules"
]
}
この段階では "strict": trueにはせず、"noImplicitThis": trueのみに留めています。導入段階から厳しいルールを設定してしまうと、既存コードの移行が厳しくなってしまうという理由です。
型検査用のscriptを定義する
前述したように、ts-loaderでは型検査を行わないため、package.jsonに手動型検査用のscriptを定義します。
{
...,
"scripts": {
...,
"type-check": "npx tsc -p . --noEmit",
}
}
npxでtypescript compilerを呼び出して型検査を行います。この際、--noEmitオプションを付与することで、ファイルのoutputを行わずに型検査のみを実行しています。
試しに既存の.jsファイルを.tsに書き換えて型検査を実行してみます。
$ npm run type-check
> project@ type-check /path/to/project/frontend
> npx tsc -p . --noEmit
src/javascripts/account/register.ts:23:13 - error TS2339: Property 'name' does not exist on type 'unknown'.
23 if (elm.name == 'authenticity_token') {
~~~~
...
Found 39 errors.
上記のように、コンパイルエラーを検査することが出来ます。
前述したように、ts-loaderでは型検査を行っていませんので、この状態でもビルドは成功します。
sfc.d.tsを作成する
Visual Studio Code等のエディター・IDEで .vueファイルのimportを解決するために、プロジェクト内に以下のようなファイルを追加します。(sfcはSingle File Componentの略です)
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
以上の修正は既にmasterブランチへマージされており、無事TypeScriptで開発できるようになりました。
既存ロジックの変更は行っていないため、障害等も特にありませんでした。
まとめ
いかがでしたでしょうか。
0→1でTypeScriptを導入することは簡単ですが、既存プロダクトに途中からTypeScriptを導入するのは心理的ハードルが高いように思えます。
本記事が、「稼動中のプロダクトにTypeScriptを導入したいけど、障害怖いしキツそうだなぁ...」といった方々の助けになれれば幸いです。
ラクスルでは、Vue.jsで新たなUIスタンダードを創るフロントエンドエンジニアを募集しています。