9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

NuxtJS(with Apollo)のTypeScript対応

Last updated at Posted at 2019-12-22

お題

前回、「frontendに「nuxtjs/apollo」、backendに「go+gqlgen」の組み合わせでGraphQLサービスを作る」というお題で、NuxtJSアプリを作った。(サーバーサイドは単に固定のJSONを返すだけのとりあえず実装だけど)
今回は、リクエストに応じた結果を返すよう、サーバーサイド(Golang)の実装を修正しようと思ったけど、その前に素のJavaScriptでは後々デバッグが厳しくなると思ったのでTypeScript対応しておこうかと。

正直、ほとんど説明レスなので参考になるかどうかは微妙なところ。
記載した環境下において、このような修正を加えると、TypeScript対応ができたという1事例として見てもらえれば。

関連記事索引

開発環境

# OS - Linux(Ubuntu)

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"

# フロントエンド

Nuxt.js

$ cat yarn.lock | grep "@nuxt/vue-app"
    "@nuxt/vue-app" "2.11.0"
"@nuxt/vue-app@2.11.0":
  resolved "https://registry.yarnpkg.com/@nuxt/vue-app/-/vue-app-2.11.0.tgz#05aa5fd7cc69bcf6a763b89c51df3bd27b58869e"

パッケージマネージャ - Yarn

$ yarn -v
1.19.2

IDE - WebStorm

WebStorm 2019.3
Build #WS-193.5233.80, built on November 25, 2019

参考

https://typescript.nuxtjs.org/ja/guide/
https://github.com/nuxt-community/nuxt-property-decorator

実践

手順ひとつひとつを書くよりも前回の記事で出来上がったソースからの差分を見る方がわかりやすいかな。

TypeScript導入

■ package.json

参考にしたサイトに記載のあったいくつかのモジュールを追加。
単純にTypeScript対応するだけでなく、GraphQL用にApolloを使っている関係でvue-apolloも追加。

diff --git a/frontend/package.json b/frontend/package.json
index 67223cb..b0b174f 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -5,7 +5,7 @@
   "author": "sky0621",
   "private": true,
   "scripts": {
-    "dev": "nuxt",
+    "dev": "nuxt --port 3000",
     "build": "nuxt build",
     "start": "nuxt start",
     "generate": "nuxt generate",
@@ -13,9 +13,13 @@
   },
   "dependencies": {
     "@nuxtjs/apollo": "^4.0.0-rc17",
-    "nuxt": "^2.0.0"
+    "@typescript-eslint/parser": "^2.12.0",
+    "nuxt": "^2.0.0",
+    "nuxt-property-decorator": "^2.5.0",
+    "vue-apollo": "^3.0.2"
   },
   "devDependencies": {
+    "@nuxt/typescript-build": "^0.5.2",
     "@nuxtjs/eslint-config": "^1.0.1",
     "@nuxtjs/eslint-module": "^1.0.0",
     "@nuxtjs/vuetify": "^1.0.0",

■ nuxt.config.js

diff --git a/frontend/nuxt.config.js b/frontend/nuxt.config.js
index 8454218..14392a5 100644
--- a/frontend/nuxt.config.js
+++ b/frontend/nuxt.config.js
@@ -3,7 +3,7 @@ import colors from 'vuetify/es5/util/colors'
 export default {
   mode: 'universal',
   /*
-   ** Headers of the page
+   ** Headers of the pagebuildModules
    */
   head: {
     titleTemplate: '%s - ' + process.env.npm_package_name,
@@ -37,7 +37,8 @@ export default {
   buildModules: [
     // Doc: https://github.com/nuxt-community/eslint-module
     '@nuxtjs/eslint-module',
-    '@nuxtjs/vuetify'
+    '@nuxtjs/vuetify',
+    '@nuxt/typescript-build'
   ],
   /*
    ** Nuxt.js modules
@@ -68,8 +69,8 @@ export default {
   apollo: {
     clientConfigs: {
       default: {
-        // Goサーバを 8080 ポートで起動する予定のため
-        httpEndpoint: 'http://localhost:8080/query'
+        // Goサーバを 5050 ポートで起動する予定のため
+        httpEndpoint: 'http://localhost:5050/query'
       }
     },
     // 任意だけど、これがないとGraphQL的なエラー起きた時に原因が掴みづらいため
@@ -80,9 +81,14 @@ export default {
    ** Build configuration
    */
   build: {
+    babel: {
+      plugins: [
+        ['@babel/plugin-proposal-decorators', { legacy: true }],
+        ['@babel/plugin-proposal-class-properties', { loose: true }]
+      ]
+    }
     /*
      ** You can extend webpack config here
      */
-    extend(config, ctx) {}
   }
 }

■ tsconfig.json

diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000..391e78e
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,34 @@
+{
+  "compilerOptions": {
+    "target": "es2018",
+    "module": "esnext",
+    "experimentalDecorators": true,
+    "moduleResolution": "node",
+    "lib": [
+      "esnext",
+      "esnext.asynciterable",
+      "dom"
+    ],
+    "esModuleInterop": true,
+    "allowJs": true,
+    "sourceMap": true,
+    "strict": true,
+    "noEmit": true,
+    "baseUrl": ".",
+    "paths": {
+      "~/*": [
+        "./*"
+      ],
+      "@/*": [
+        "./*"
+      ]
+    },
+    "types": [
+      "@types/node",
+      "@nuxt/types"
+    ]
+  },
+  "exclude": [
+    "node_modules"
+  ]
+}

■ vue-shim.d.ts

diff --git a/frontend/vue-shim.d.ts b/frontend/vue-shim.d.ts
new file mode 100644
index 0000000..eb40980
--- /dev/null
+++ b/frontend/vue-shim.d.ts
@@ -0,0 +1,4 @@
+declare module "*.vue" {
+  import Vue from 'vue'
+  export default Vue
+}

■ .eslintrc.js

diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js
index 6b8177c..f30bc4f 100644
--- a/frontend/.eslintrc.js
+++ b/frontend/.eslintrc.js
@@ -5,7 +5,7 @@ module.exports = {
     node: true
   },
   parserOptions: {
-    parser: 'babel-eslint'
+    parser: '@typescript-eslint/parser'
   },
   extends: [
     '@nuxtjs',

■ pages/index.vue

diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue
index 7587e44..c8a8896 100644
--- a/frontend/pages/index.vue
+++ b/frontend/pages/index.vue
@@ -4,10 +4,13 @@
   </div>
 </template>

-<script>
+<script lang="ts">
+import { Vue, Component } from 'nuxt-property-decorator'
+import 'vue-apollo'
 import TodoCard from '~/components/TodoCard.vue'

-export default {
+@Component({
   components: { TodoCard }
-}
+})
+export default class IndexPage extends Vue {}
 </script>

■ components/TodoCard.vue

diff --git a/frontend/components/TodoCard.vue b/frontend/components/TodoCard.vue
index 5bdbc66..d740996 100644
--- a/frontend/components/TodoCard.vue
+++ b/frontend/components/TodoCard.vue
@@ -25,20 +25,32 @@
   </div>
 </template>

-<script>
+<script lang="ts">
+import { Vue, Component } from 'nuxt-property-decorator'
+import 'vue-apollo'
 import todos from '~/apollo/queries/todos.gql'
-export default {
-  data() {
-    return {
-      todos: []
-    }
-  },

+interface User {
+  id: String
+  name: String
+}
+
+interface Todo {
+  id: String
+  text: String
+  done: Boolean
+  user: User
+}
+
+@Component({
   apollo: {
     todos: {
       prefetch: true,
       query: todos
     }
   }
+})
+export default class TodoCard extends Vue {
+  todos: Todo[] = []
 }
 </script>

TypeScript対応後のソース

実際のソースは対応後のソースを見た方がわかりやすい気がする。
せっかくTypeScript化するので、classベースの書き方にした。

[pages/index.vue]
<template>
  <div>
    <TodoCard />
  </div>
</template>

<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
import 'vue-apollo'
import TodoCard from '~/components/TodoCard.vue'

@Component({
  components: { TodoCard }
})
export default class IndexPage extends Vue {}
</script>
[components/TodoCard.vue]
<template>
  <div>
    <v-row>
      <v-col cols="12" sm="6" offset-sm="3">
        <v-card>
          <v-list two-line subheader>
            <v-list-item v-for="todo in todos" :key="todo.id" link>
              <v-list-item-avatar>
                <v-icon>mdi-gift-outline</v-icon>
              </v-list-item-avatar>
              <v-list-item-content>
                <v-list-item-title>{{ todo.text }}</v-list-item-title>
                <v-list-item-subtitle>{{ todo.done }}</v-list-item-subtitle>
              </v-list-item-content>
              <v-list-item-content>
                <v-list-item-title>
                  {{ todo.user.name }}
                </v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list>
        </v-card>
      </v-col>
    </v-row>
  </div>
</template>

<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
import 'vue-apollo'
import todos from '~/apollo/queries/todos.gql'

interface User {
  id: String
  name: String
}

interface Todo {
  id: String
  text: String
  done: Boolean
  user: User
}

@Component({
  apollo: {
    todos: {
      prefetch: true,
      query: todos
    }
  }
})
export default class TodoCard extends Vue {
  todos: Todo[] = []
}
</script>

まとめ

これで、ローカルで前回と同様に動作確認できた。
今回はGraphQLを介して取得したレスポンスの型を自前でinterfaceとして定義したけど、GraphQLスキーマから自動生成した方がよいので、そのあたりは次回以降、試みるかも。
あと、nuxt-config.jsなど、結局JSのままにしてるファイルがあるので、そのへんもどうせだったらTSに変えておきたい。

今回の全ソースは下記。
https://github.com/sky0621/study-graphql/tree/v0.2.0

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?