8
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 5 years have passed since last update.

もしもVue.jsにTypescriptを導入したら

8
Posted at
Page 1 of 42

もしもVue.jsにTypescriptを導入したら ①調査編


まずは現行環境を整理

  • Laravel 6
  • Vue 2.5
  • vuex 3.0
  • vue-router 3.0
  • element-ui 2.4

とりあえず調べてみる

  • VueにTypescriptを導入するにはVue 2.5以上が必要。→これはOK
  • Typescript 2.4以上が必要。→なるほど
  • そしてTypescriptを使うには3パターンがある
  1. Vue.extendを使う
  2. vue-class-componentを使う
  3. vue-property-decoratorを使う

Vue公式

やって来る Vue 2.5 での TypeScript の変更
https://jp.vuejs.org/2017/09/23/upcoming-typescript-changes-in-vue-2.5/


1. Vue.extend

Vue公式より

Vue コンポーネントオプション内部で TypeScript が型を適切に推測できるようにするには、Vue.component または Vue.extend でコンポーネントを定義する必要があります:



つまり〜

Vue コンポーネントオプション内部(dataとかpropsとかmethod内)でTypescriptが型を推測できるようにするには、Vue.extendまたはVue.component(内部的にVue.extendを呼んでいる)でコンポーネントを定義する必要があります

要はTypescript使うならVue.extendしろと


![](https://i.imgur.com/vUGTe59.png? =100x)


Vue公式


2. vue-class-component

公式ドキュメントより

Vueクラスコンポーネントは、クラススタイルの構文でVueコンポーネントを作成できるライブラリです。

つまりライブラリです


GitHub
https://github.com/vuejs/vue-class-component

公式ドキュメント
https://class-component.vuejs.org/


3. vue-property-decorator

GitHubより

このライブラリは、vue-class-componentに完全に依存しているため、このライブラリを使用する前に、READMEをお読みください。

つまりvue-class-componentの拡張版ライブラリです。


GitHub
https://github.com/kaorun343/vue-property-decorator


で?結局どれ使えばいいの?


さらに調べてみる

  • "vue typescript"で検索するとvue-property-decoratorが一番ヒットするのでさらに調べてみる。
  • どうやらこのライブラリはtypescriptの機能である「デコレータ」を使ってかけるものらしい。

デコレータとは、クラス宣言とメンバーの注釈とメタプログラミング構文の両方を追加する方法を提供する

Typescript公式ドキュメント



まあとりあえずどんな感じに書くの?


import { Vue, Component, Prop } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @Prop(Number) readonly propA: number | undefined
  @Prop({ default: 'default value' }) readonly propB!: string
  @Prop([String, Boolean]) readonly propC: string | boolean | undefined
}
import { Vue, Component, Emit } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  count = 0

  @Emit()
  addToCount(n: number) {
    this.count += n
  }

  @Emit('reset')
  resetCount() {
    this.count = 0
  }

  @Emit()
  returnValue() {
    return 10
  }

  @Emit()
  onInputChange(e) {
    return e.target.value
  }

  @Emit()
  promise() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(20)
      }, 0)
    })
  }
}


もっと簡単に書けないのか・・・

※ちなみに@xxxがクラスデコレータ、メソッドデコレータというやつらしい。@の下のクラスやメソッドを@xxx側で監視、変更、または置換できるらしい

詳しくは公式ドキュメントをチェック
https://www.typescriptlang.org/docs/handbook/decorators.html


じゃあ次にvue-class-componentを使うとするとどうなる?

  1. Vue.extendだけ使う
  2. vue-class-componentを使う
  3. vue-property-decoratorを使う

このvue-class-componentだけ使おうとする時に、気をつけなければいけない点があります。

それはpropsやwatchはそのまま使えないので、一度componentとして定義するか、@Componentのデコレータの引数に渡してあげないといけません。

Props Definition
https://class-component.vuejs.org/guide/props-definition.html


こんな感じにする必要があるらしく

import Vue from 'vue'
import Component from 'vue-class-component'

// Define the props by using Vue's canonical way.
const GreetingProps = Vue.extend({
  props: {
    name: String
  }
})

// Use defined props by extending GreetingProps.
@Component
export default class Greeting extends GreetingProps {
  get message(): string {
    // this.name will be typed
    return 'Hello, ' + this.name
  }
}

ちょっと使いにくいです。。。

それらをデコレータで使いやすく、ひとつのcomponentで定義できるようにしたのがvue-property-decoratorです

なので選択肢としてはVue.extendだけかvue-property-decoratorを使うかの2択になっているようです

ということでVue.extendだけ使う方法でTypescriptを導入してみたいと思います

  1. Vue.extendだけ使う
  2. vue-class-componentを使う
  3. vue-property-decoratorを使う

もしもVue.jsにTypescriptを導入したら ②実践編


とりあえずvue createで作ったVue+TypescriptのサンプルPJと、マイクロソフト様が書いたありがたいドキュメント等を参考にしていきます

TypeScript Vue Starter
https://github.com/microsoft/TypeScript-Vue-Starter
TypeScript のサポート
https://jp.vuejs.org/v2/guide/typescript.html


まずは初期設定

  • npm i -D typescript ts-loaderで必要なモジュールをインストール
  • ./node_modules/.bin/tsc --versionでインストール確認

tsconfig.json追加

詳細は公式ドキュメント参照

{
  "compilerOptions": {
    "target": "es5",
    "module": "es6",
    "strict": true,
    "importHelpers": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "lib": [
      "es6",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "resources/js/**/*.ts",
    "resources/js/**/*.tsx",
    "resources/js/**/*.vue",
  ],
  "exclude": [
    "node_modules"
  ]
}

Typescript公式ドキュメント

tsconfig.json
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
Compiler Options
https://www.typescriptlang.org/docs/handbook/compiler-options.html


webpack.mix.jsを修正

- mix.js('resources/js/tr/app.js', 'public/js/app-tr.js')
+ mix.ts('resources/js/tr/app.ts', 'public/js/app-tr.js')


vue-shims.d.tsファイルを追加

.vueファイルがなんであるかをTypescritpに伝えるためにvue-shims.d.tsファイルを追加する必要があるらしいので追加。includeするディレクトリ配下で良いらしい

// resources/js/shims-vue.d.ts

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

app.jsをTypescriptに書き換え ポイント

  • TypeScript未対応のjsをimportしようとすると定義ファイルが見つからないエラーになるので一旦requireなどで対応する。(ベストはd.tsの定義ファイルを作ること)
  • さらにrequireを使うためにはnodeの定義ファイルが必要になるのでnpm i @types/nodeでインストール
  • vueファイルをimportする際は拡張子をつける必要あり

router.jsをTypescriptに書き換え

RouteConfig型で作成する必要があるらしい

▼サンプルPJ

import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

  const routes: Array<RouteConfig> = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router


つらみポイント

  • 既存ライブラリの型定義ファイル(d.ts)に沿った使い方をしていない場合(型が違う)や、そもそもライブラリの定義ファイルが足りていなかったりする場合などは自前で型定義を拡張しなければならない
  • その場合はdeclare(アンビエント宣言)を拡張します。コレをDeclaration Mergingというらしい

こんな感じに

declare module 'element-ui/types/message' {
    interface ElMessage {
        closeAll(): void
    }
}

既存ライブラリのクラス拡張
https://kakkoyakakko2.hatenablog.com/entry/2018/06/28/003000


色々試行錯誤した結果・・・

動いた!!ログインもできた


![](https://i.imgur.com/vUGTe59.png? =200x)


まとめ

  • 既存のVueをts化は一応できそう
  • ただ既存ライブラリが絡む部分はかなりハマった(どういう型指定するば良いのか分からない)
  • 今回は付け焼き刃的に対応してみたが、Typescriptの基礎を知らないと辛い
  • 正直Vue3.0で正式対応版きたらそれにのっかるのがベストだと思う

おわり


今回参考にしたページ

8
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
8
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?