くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

NuxtをTypeScript化するときのやりかた

Vue.jsとNuxt.jsでいろいろ作っているけれど、TypeScriptが素敵すぎる。。
create-nuxt-appだとTypeScriptがないので、TypeScript化するときにやることを整理したときのメモ

TypeScriptが使えるように設定する

0. ディレクトリ構成

ディレクトリ構成はこんな感じ。

  • プロジェクトはcreate-nuxt-appで作成
  • Firebase Functionsも使うので、app配下をsrcDirに変更している
.
├── app            ... NuxtのsrcDir
│   ├── assets
│   ├── components
│   ├── layouts
│   ├── middleware
│   ├── pages
│   ├── plugins
│   ├── static
│   └── store
├── functions      ... Firebase FunctionのsrcDir
│   ├── src
│   ├── package.json
│   └── tsconfig.json
├── nuxt.config.js
└── package.json

1. npmでパッケージのインストール

$ npm install --save nuxt-property-decorator vuex-class
$ npm install --save -D @types/node ts-loader typescript

2. index.d.tsの追加

TypeScriptで.vueファイルを利用できるようにapp/index.d.tsを追加する

declare module "*.vue" {
  import Vue from "vue";
  const _default: Vue;
  export default _default;
}

3. typesを配置するディレクトリを用意

nuxt-community/typescript-templateを参考に、 app/typesディレクトリと、以下のファイルを作成

  1. app/types/index.ts ... 空ファイル
  2. app/types/state.ts
export * from "./state";

4. modules/typescript.jsを追加

nuxt-community/typescript-templateのものをコピーしてくる。

export default function() {
  // Add .ts & .tsx extension to Nuxt
  this.nuxt.options.extensions.push("ts", "tsx");

  // Extend webpack build
  this.extendBuild(config => {
    // Add TypeScript
    config.module.rules.push({
      test: /\.tsx?$/,
      loader: "ts-loader",
      options: { appendTsSuffixTo: [/\.vue$/] }
    });

    // Add .ts extension in webpack resolve
    if (!config.resolve.extensions.includes(".ts")) {
      config.resolve.extensions.push(".ts");
    }

    // Add .tsx extension in webpack resolve
    if (!config.resolve.extensions.includes(".tsx")) {
      config.resolve.extensions.push(".tsx");
    }
  });
}

追加したモジュールをnuxt.config.jsに追加

module.exports = {
  modules: [
    "~/modules/typescript.js"
  ]
}

5. tsconfig.jsonを追加

これもnuxt-community/typescript-templateのものをコピーしてくる。

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "es2015"],
    "module": "es2015",
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "noImplicitAny": false,
    "noImplicitThis": false,
    "strictNullChecks": true,
    "removeComments": true,
    "suppressImplicitAnyIndexErrors": true,
    "allowSyntheticDefaultImports": true,
    "allowJs": false,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./app/*"]
    }
  }
}

6. 設定後のディレクトリ構成

設定はこれで完了。変更後のディレクトリ構成はこんな感じ。

.
├── app            ... NuxtのsrcDir
│   ├── assets
│   ├── components
│   ├── layouts
│   ├── middleware
│   ├── modules
│   │   └── ★typescript.js
│   ├── pages
│   ├── plugins
│   ├── static
│   ├── store
│   ├── ★types
│   │   ├── ★index.ts
│   │   └── ★state.ts
│   └── ★index.d.ts
├── functions      ... Firebase FunctionのsrcDir
│   ├── src
│   ├── package.json
│   └── tsconfig.json
├── ★nuxt.config.js
├── package.json
└── ★tsconfig.json

.vueファイルの内容をTypeScriptに変更する

あとは、各.vueファイルを変更していく。

変更前

<script>
export default {
  head: function() {
    return { title: "タイトル" }
  },
  
  props: {
    name: { type: string, default: "なまえ" }
  },
  
  data(): {
    return {
      flag: false
    };
  },
  
  computed: {
    isEmptyName: function() {
      return this.name === "";
    }
  },
  
  methods: {
    toggleFlag: function() {
      this.flag = !this.flag;
    }
  }
}
</script>

変更後

  • Componentは、@Componentに設定する
  • data/propsは、fieldとして定義。props@Prop()が必要
  • computedは、getアクセサとして定義
  • methodやライフサイクルの関数は、methodとして設定する
<script lang="ts">
import { Component, Vue } from "nuxt-property-decorator";

@Component({}) // ※必須. コンポーネントを使ってなくても必要
export default class  extends Vue {
  /** head() */
  private head() {
    return { title: "タイトル" });
  }
  
  /** prop() */
  @Prop() name: string = "なまえ";

  /** data() */
  private flag: boolean = false;

  /** computed() */
  public get isEmptyName(): boolean {
    return this.name === ""; 
  }
  
  /** methods() */
  private toggleFlag(): void {
    this.flag = !this.flag;
  }
}
</script>

以上!! 型のある開発はやっぱり素敵

参考にしたサイト様