これは 2019.01.29 スタートアップテック vol.2 の LT 資料です
Vue で TypeScript を使いたい
Vue は楽しいです。TypeScript も楽しいです。楽しい * 楽しいにしたいです。
現状確認および課題
- Vue だけであれば VueCLI で TypeScript サポート選べば OK
- Vuex も TypeScript で書ける
- けどコンポーネントからの読み出しは恩恵を受けにくい
- Nuxt も v2.5 から TypeScript サポートが良くなる
- 初期設定なしですぐ書けるようになる
- Vue template 内でのサジェストが弱い…
今回の目標
- とにかくサジェストされてほしい。いつでもどこでも
まずは VueCLI で TypeScript / Class スタイルな設定でプロジェクトを作る
$ vue create ts-proj
? Check the features needed for your project:
#=> Babel, TypeScript, Router, Vuex あたり
? Use class-style component syntax?
#=> Yes
? Use Babel alongside TypeScript for auto-detected polyfills?
#=> Yes
<template>
<div class="home">
<ul>
<li v-for="todo in todos" :key="todo.id">{{todo.content}}</li>
</ul>
</div>
</template>
<script lang="ts">
import {Component, Provide, Vue} from 'vue-property-decorator'
interface Todo {
id: number
content: string
}
@Component({})
export default class Home extends Vue {
@Provide() todos: Todo[]
}
</script>
普通の SFC で定義した配列
サジェストされてる! (なぜか VScode はされなかった…)
次は vuex で試してみる。 store.ts
に適当に state を生やしてみる。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
interface VuexTodo {
id: number
content: string
}
export interface State {
vuex_todos: VuexTodo[]
}
export const state: State = {
vuex_todos: []
}
export default new Vuex.Store({ state })
適当に shims-vue.d.ts
とかで declare 拡張しておく
import Vue from 'vue'
import * as Vuex from 'vuex'
import * as Store from './store'
declare module 'vue/types/vue' {
interface Vue {
$store: Vuex.Store<any>,
$state: Store.State
}
}
<template>
<div class="home">
<ul>
<li v-for="todo in allTodo" :key="todo.id">{{todo.content}}</li>
</ul>
</div>
</template>
<script lang="ts">
import {Component, Provide, Vue} from 'vue-property-decorator';
@Component({})
export default class Home extends Vue {
get allTodo () {
return this.$state.vuex_todos
}
}
</script>
this.$state
経由は拡張しているのでサジェストされる。でも template 内だとされない
あと this.$store
経由の getters などはサジェストされない ( any にしてるから… )
state はサジェストされるが… getters this.$store.getters['getAllTodo']
とかは厳しい。そこで次は vuex-module-decorators
を入れてみる
this.$store
経由ではアクセスしないので shims-vue.d.ts
を修正
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
import { Module, VuexModule, getModule } from 'vuex-module-decorators'
import store from "@/store"
interface ModuleTodo {
id: number
content: string
}
export interface ITodo {
module_todos: ModuleTodo[]
}
@Module({ dynamic: true, store, name: "counter", namespaced: true })
class Todo extends VuexModule implements ITodo{
module_todos: ModuleTodo[] = [] // state
get getAllModuleTodo () { // getter
return this.module_todos
}
}
export const storeTodo = getModule(Todo)
Vuex store 初期化
import Vue from 'vue'
import Vuex from 'vuex'
import { ITodo } from './store/todo'
Vue.use(Vuex)
export interface State {
todo: ITodo
}
export default new Vuex.Store<State>({})
コンポーネント側で読み込んで使う。
import {Component, Provide, Vue} from 'vue-property-decorator';
import {storeTodo} from '@/module/todo'
@Component({})
export default class Home extends Vue {
get allTodo () {
return storeTodo.getAllModuleTodo
}
}
これで getters とか actions もサジェストされる!
でも template ではまだサジェストされない
次は template でもサジェストがほしい。というわけで jsx (tsx) で書くようにしてみる。 @vue/cli-plugin-babel
が必要
tsconfig.json で設定
{
"compilerOptions": {
"jsx": "preserve",
"jsxFactory": "h",
// ...
}
}
Home.tsx
import { VNode, CreateElement } from 'vue';
import { Component, Vue } from 'vue-property-decorator';
import { storeTodo } from '@/module/todo'
@Component
export default class Todo extends Vue {
public render(h: CreateElement): VNode {
return <div>
<ul>
{storeTodo.getAllModuleTodo.map(todo =>
<li>{todo.content}</li>
)}
</ul>
</div>
}
}
これで template でもサジェストされた!最高!
( JSX だから当然だけど )
注意
- JSX 採用すると Vue 独自の便利機能が使えない
-
v-model
とかイベント系を自前で実装する必要がある -
vue/jsx
が beta だけど一応対応実装進めているので時間の問題?
-