TypescriptでComposition API(Vue3)を型付きサクサクで使う

# はじめに

Vue3で導入されたComposition API。使ってみて、Componentが簡潔になることを実感。あと、データごとに関心事を分けて書くことができ、スッキリかけて気持ちが良い。

一方、型付きにするにはコツがあるのでそれを書く。型推論が効いた状態って贅沢感があっていいです。

# コード

## reactiveを型付きにする

ジェネリックで型を教えると良い

```tsx
const strRef = ref<string>('Hello');
const state = reactive<{ name: string, age: number }>({ name: 'taro', age: 10 });
```

reactiveだと冗長な感じがする。何より読みづらいから型部分は切り出すと良い感じ。

```tsx
interface Person {
  name: string,
  age: number
}
```

とコンポーネントの頭当たりで定義して

```tsx
const state = reactive<Person>({ name: 'taro', age: 10});
```

というような使い方をする。

定義部分が離れちゃうのは拡張する際にめんどくさいので、機能拡張が欲しい感じ。

型名を `Person`ってしたけど、スコープがコンポーネント内に限られているので単純に `State` でも良い気もする。(迷ってる。)

## propsを型付きにする

Propsの型定義は特にComposition APIだからって変わらないですが、自分用のメモとして書く。

```tsx
props {
  person: Object as PropType<Person>,
  persons: [] as PropType<Person[]>
}
```

配列やオブジェクトの場合、PropTypeを経由しないと型情報とみなされないようなので注意が必要。

(ComputedのときはRefになるんだっけ、調べなきゃ)

## モジュール

Composition APIの良いところであるモジュール化なんだけど、実際使うときには注意が必要。

何かって言うと、 `setup()`と同じように、生成にReactiveとComputedを定義する必要があるっていうこと。実行順を考えないとReactiveが動作しないという問題がある。

つまり、クラス定義を行う場合コンストラクタが肥大化するという問題がある。コンストラクタで行っても良いんだけど、インターフェイスに則った `Factory method`を作ったほうが読みやすいように思える。

```tsx
interface Person {
  name: reactive<string>,
  age: Ref<number>,
  greet: Computed<string>
}

getPerson(name: string, age: number) {
	const nameRef = ref<string>(name);
  const ageRef = ref<number>(age);
  const greet = Computed(() => `Hello ${nameRef.value}!`;
  return { name: nameRef, age: ageRef, greet };
}
```

このように意外とスッキリ書ける。 

メソッド内で関数書くことに抵抗がない言語なので、意外と `Factory method`がクラスの定義と違いが少ない気がする。インターフェイスは冗長な感じになるけど。。。

## サンプル

とりあえず、CodePenに[(サンプル)](https://codepen.io/iwamoto-takaaki/pen/XWXvOwQ?editors=0111)を作った。
![スクリーンショット 2020-08-06 16.22.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310093/edfe845f-ec4e-3257-9838-c8cb2ea8be64.png)

見どころとしては、`person`を集約する `persons`クラスを定義している点。Composition APIの良い点だと思うのだけど、reactiveを入れ子にすることができる。

```tsx
interface Person {
  name: Ref<string>,
  age: Ref<number>,
  greet: Computed<string>,
};

interface Persons {
  persons: reactive<Person[]>,
  add: (person: Person) => void,
  remove: (person: Person) => void,
};
```

`persons` はおおもとのコンポーネントである `my-component`で利用され、 `person`は `person`コンポーネントで利用され、コンポーネントでなんの設定もせず、それぞれreactiveとして利用できる。

これのおかげで、コンポーネントは関数や変数のマッピングのみとなる。

```tsx
app.component('person', {
  template: // 省略
  ,
  props: {
    person: Object as PropType<Person>
  },
  setup(props, { emit }) {
    const mode = ref<'view' | 'edit'>('view');
    const remove = () => emit('remove', props.person);
    const pushEdit = () => mode.value = 'edit';
    const pushUpdate = () => mode.value = 'view';
    return { remove, pushEdit, pushUpdate, mode };
  }
});
```

この利点は、再利用というよりコンポーネントからロジックをモデルとして切り出すことによって見通しがよくなる点と、テストが容易なる利点がある。

やっててちょっと嫌になるのは配列の扱い。

```tsx
const remove = (person: Person) => {
    const index = persons.value.indexOf(person);
    persons.value.splice(index, 1);
  };
```

依存関係の更新を行うために、spliceで削除を行う必要がある。

ひと目で削除しているように見えないので、なにか機能拡張を用意したいなぁ。

# 終わりに

元々はVuexで型付でやろうとしていたのだけど、モジュール化がきれいにできないことが判ってきて、Coposition APIが依存関係がわかりやすいので試してみたら良かった。

また、モジュールの分割が容易なのは非常に助かった。

Typescriptとしては、奇妙な書き方になってしまうが、なれてしまえばなんの問題も無い。

結論としては、非常におすすめ！！

# 参考
ありがとうございます！
Vue Mastery: https://www.vuemastery.com/courses/vue-3-reactivity
[先取りVue 3.x !! Composition API を試してみる](https://qiita.com/ryo2132/items/f055679e9974dbc3f977)
[Vue.js の Composition API における親子コンポーネント間のデータ受け渡し](https://qiita.com/jay-es/items/8b3b2bf06eeefdc43a5a)
[vue-composition-apiを使って機能単位のコード分割と合成を試してみた](https://qiita.com/s_nagasawa/items/ef70032f996face318e5)
[Vue + TypeScriptでpropsのObjectやArrayに型をつける](https://qiita.com/iMasanari/items/31d8a26c7ee22793585c)
[Ref vs Reactive Vue3 Composition APIのリアクティブ関数の探究 / ref vs reactive Vue Composition API Deep in
](https://speakerdeck.com/kawamataryo/ref-vs-reactive-vue-composition-api-deep-in)