Nuxt2 + TypeScriptの環境構築
この記事はKaigen Discord Advent Calendar 2018 13日目の記事です。
前置き
Nuxt.jsでTypeScriptを導入した環境を構築するのにかなり苦労したので同じことをしようと思う人と自分に向けてまとめておく。
最終的なテンプレートはGitHubにあげていますのでご参考に。
環境
- Node.js: 10.14.1
- Nuxt: 2.3.4
構築過程
プロジェクト作成
まず
$ yarn create nuxt-app
でNuxtプロジェクトを作る。構成は以下の画像のようにした。
今回は環境構築だけなのでサーバサイドフレームワークやUIフレームワークは使わず, また, APIをいじいじするわけでもないのでAxiosも入れない。Universalアプリケーション作るわけでもないのでSPA。lintはかけたいのでeslintやprettierは今回のプロジェクトで導入した。
プロジェクトを作成したらとりあえず
$ yarn run dev
で動かしてみる。
Nuxt v2.3.4ではこのような画面が出たら成功。
TypeScriptの導入
TypeScriptとWebpack用のloaderとnodeの型宣言ファイルを導入する
$ yarn add -D typescript ts-loader @types/node
Nuxt(というよりかはVueファイル)で使用するパッケージを導入
$ yarn add vue-class-component vue-property-decorator vuex-class
vue-class-componentやvue-property-decoratorをベースとしたnuxt-class-componentやnuxt-property-decoratorがnuxt-communityで公開されているがどちらともメンテナンスが滞ってる(nuxt-property-decoratorは最近メンテナンスが再開された?)し, どちらともベースとなるもので事足りるのとそっちの方が機能が多いので別に導入しなくていいと思う。
TypeScriptをビルドできるようにする
TypeScriptをビルドできるようにWebpackの設定をいじってみる。
Nuxtではnuxt.config.js
のbuildプロパティ*1でWebpackの設定をいじるほか, モジュール*2という形でもWebpackの設定をいじることができる。
前者の方法では軽く設定を変える程度で複数プロジェクトで同じようなことをしない場合, そうではない場合は後者の方法を用いるのがいいでしょう。
今回のような場合は後者の方法で実装したほうがよさそう。
幸いにもNuxt2ではないがTypeScriptのテンプレートがあったのでそれを参考にNuxt2用に実装してみる。
まず, modules/typescript.js
とmodulesフォルダとjsファイルを作り以下のコードを記入する。(一応, tsxにも対応できるようにした)
module.exports = function() { // Nuxtで.ts/.tsxファイルを解決させる this.nuxt.options.extensions.push('ts', 'tsx') // Webpackの設定 this.extendBuild(config => { // .ts/.tsxをビルドできるようにts-loaderを追加する。 config.module.rules.push({ test: /\.tsx?$/, exclude: /(node_modules)/, loader: 'ts-loader', options: { appendTsSuffixTo: [/\.vue$/] } }) // Webpackのextensionsに.tsが入っていない場合解決できるようにする if (!config.resolve.extensions.includes('.ts')) { config.resolve.extensions.push('.ts') } // Webpackのextensionsに.tsxが入っていない場合解決できるようにする if (!config.resolve.extensions.includes('.tsx')) { config.resolve.extensions.push('.tsx') } }) }
次にモジュールを読み込めるようにnuxt.config.js
に以下を追記する。
modules: ['~modules/typescript.js']
tsconfig.json
は以下のようにした。
{ "compilerOptions": { "target": "es5", "lib": ["dom", "es2015", "es2016", "es2017"], "module": "es2015", "moduleResolution": "node", "alwaysStrict": true, "experimentalDecorators": true, "noImplicitAny": false, "noImplicitThis": true, "strictNullChecks": true, "sourceMap": true, "removeComments": true, "suppressImplicitAnyIndexErrors": true, "allowSyntheticDefaultImports": true, "allowJs": true, "baseUrl": ".", "paths": { "~/*": ["./*"], "@/*": ["./*"] }, "typeRoots": [ "node_modules/@types", "types" ] }, "exclude": [ "node_modules" ] }
vscodeだと以下のようなファイルエラーが出ますが無視して構いません。(理由はよくわからないです…分かる人は教えてくれれば幸いです。)
[ts] 入力ファイルを上書きすることになるため、ファイル <rootPath>/modules/typescript.js' を書き込めません。
2/20追記: コメントにて
tsconfing.jsonのcompilerOptionsにoutdirかoutFileを指定するとエラーがなくなるようです。
変換しない場合は、"outFile": "" とするかまたは存在しないデタラメなフォルダを指定すればいいようです。
という指摘をいただきました。
確認はまだしていないのですがこれでそのvscodeのエラーは回避できるようです。
7ccさんありがとうございます!
最後に, VueでTypeScriptが使用できるように型宣言ファイルをtypes/index.d.ts
のように作成して以下を記入する。
declare module '*.vue' { import Vue from 'vue' export default Vue }
TypeScriptを使用する
pages/index.vue
でTypeScriptを使用してみる。ソースは以下のようにした。
<template> <section class="container"> <div> <logo/> <h1 class="title"> {{ title }} </h1> <h2 class="subtitle"> {{ fuwa }} </h2> <div class="links"> <a href="https://nuxtjs.org/" target="_blank" class="button--green">Documentation</a> <a href="https://github.com/nuxt/nuxt.js" target="_blank" class="button--grey">GitHub</a> </div> </div> </section> </template> <script lang="ts"> import Vue from 'vue' import Component from 'vue-class-component' import Logo from '~/components/Logo.vue' @Component({ components: { Logo } }) export default class extends Vue { title = 'hoge' desc = 'fuwa' } </script> <style> .container { min-height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; } .title { font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; display: block; font-weight: 300; font-size: 100px; color: #35495e; letter-spacing: 1px; } .subtitle { font-weight: 300; font-size: 42px; color: #526488; word-spacing: 5px; padding-bottom: 15px; } .links { padding-top: 15px; } </style>
ビルドしてみる
一度, yarn run lint --fix
とyarn run dev
をしてビルドしてみよう。
localhost:3000
に接続してみて以下の画像のようになったら成功。
これでTypeScriptの導入は終了した。
TypeScriptにlintをかける
TypeScriptを導入したはいいけれども.ts/.tsxファイルや.vueファイル内のscript部分にはlintはかからない。
そこでtslintやtypescript-eslint-parserを導入してlintをかけようと思う。今回はeslintを導入しているのとtslintとvueの相性が悪いのでtypescript-eslint-parserを使ってlintをかける。
まず必要となるパッケージを追加する。
$ yarn add -D typescript-eslint-parser eslint-plugin-typescript
nuxt.config.js
の
config.module.rules.push({ enforce: 'pre', test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /(node_modules)/ })
を以下のように変更
config.module.rules.push({ enforce: 'pre', test: /\.(js|ts|vue)$/, loader: 'eslint-loader', exclude: /(node_modules)/ })
.eslintrc.js
に以下を追記
overrides: [ { files: ["*.ts", "*.vue"], parserOptions: { parser: "typescript-eslint-parser" }, plugins: ['vue', 'prettier', 'typescript'] } ]
これでTypeScriptにlintがかかるようになった。
余談
この記事を作るきっかけとしてyarn create nuxt-app
でeslintとprettierを導入して初期の状態でビルドするとエラーを吐いて導入できなかったのがあった。
原因は.vueファイル内のscriptの言語がTypeScriptだと初期のeslintのパーサであるbabel-eslintで解析してしまうためそれを回避するために記事中に書いたTypeScriptにlintをかけるような方法を取らなければいけなかった。
この記事を書いてる最中にNuxtのバージョンが上がったため新しいバージョンで試したところ, エラーを吐かずにすんなりビルドを通した。
今までの苦労はなんだったんだろう…。