JavaScript の Vuex で mapGetters や mapActions などの候補を表示させる helper パターン (Type Vuex without TypeScript 参考)

# JavaScript の Vuex で mapGetters や mapActions などの候補を表示させる helper パターン

この記事は [Type Vuex without TypeScript - ITNEXT](https://itnext.io/type-vuex-without-typescript-4f183b83a907) を参考にしています。
基本的な内容はリンク先にて確認したほうがよいと思われます。
[Type Vuex without TypeScript - ITNEXT](https://itnext.io/type-vuex-without-typescript-4f183b83a907) では [chrisvfritz/vue-enterprise-boilerplate](https://github.com/chrisvfritz/vue-enterprise-boilerplate/blob/master/src/state/helpers.js) を紹介しており
`mapGetters` などの具体的使用例として挙げています。

## [Type Vuex without TypeScript - ITNEXT](https://itnext.io/type-vuex-without-typescript-4f183b83a907)

上記の記事の概要としては大体以下のような感じのようです。

* TypeScript を使用せず Vuex の mapGetters や mapActions を Vue コンポーネント使用時に定義内容を候補をインテリセンスで表示させたい
* プラグインをできるだけ使わないとなると [chrisvfritz/vue-enterprise-boilerplate](https://github.com/chrisvfritz/vue-enterprise-boilerplate/blob/master/src/state/helpers.js) の `Chris’ helper pattern` のような形にになる
* JSDoc で注釈をつけてあげれば型の表示なども可能
* もし Vuex に `breaking changes` が適用された場合には事例内の `helper.js` の定義内で吸収が可能かも

## `keyof` との組み合わせ

紹介されている事例では以下のような記述に対し `authComputed` や `authMethods` に JSDoc により注釈をつけるというものです。

```js:chrisvfritz/vue-enterprise-boilerplate/src/state/helpers.js
import { mapState, mapGetters, mapActions } from 'vuex'

export const authComputed = {
  ...mapState('auth', {
    currentUser: (state) => state.currentUser,
  }),
  ...mapGetters('auth', ['loggedIn']),
}

export const authMethods = mapActions('auth', ['logIn', 'logOut'])
```

これだと一つ一つ定義していかなければならないので・・・
`State`, `getters`, `actions` を `import` してそれぞれ `mapGetters` などに渡してしまいます。
`State` というのは [こちらの構成](https://qiita.com/ysKuga/items/6ded6a261257f143350e#profilestate-%E3%81%A8%E5%90%8C%E3%81%98%E4%BD%8D%E7%BD%AE%E3%81%A5%E3%81%91%E3%81%AE%E3%82%82%E3%81%AE) にしているものです。
Nuxt.js にてやってます。

```js:store/profile/helper.js
import { mapState, mapGetters, mapActions } from 'vuex';
// eslint-disable-next-line
import { State } from './state';
import getters from './getters';
import actions from './actions';

// eslint-disable-next-line
const stateAlias = {
  currentUser: 'namae',
};

/**
 * @type {{[K in keyof stateAlias]: State[stateAlias[K]]} & {[K in keyof getters]: getters[K]}}
 */
export const profileComputed = {
  ...mapState('profile', {
    currentUser: state => state.namae,
  }),
  ...mapGetters('profile', Object.keys(getters)),
};

/**
 * @type {{[K in keyof actions]: actions[K]}}
 */
export const profileMethods = mapActions('profile', Object.keys(actions));
```

これを実際に使ってみると・・・

```vue
<template>
  <div>
    infer js blank {{ $store.getters['profile/getNamae'] }} {{ currentUser }}
  </div>
</template>

<script>
import { profileComputed, profileMethods } from '@/store/profile/helper';

export default {
  computed: {
    ...profileComputed,
  },
  mounted() {
    this;
  },
  methods: {
    ...profileMethods,
  },
};
</script>
```

![this](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116435/aa279245-b910-07ec-7ee7-1c4d28a33e9f.jpeg)

なんか・・・型があやしいですが・・・一応推論されています。

![inferred-action](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116435/cf170835-6d0c-db05-a605-cca98cfb8a70.jpeg)

インテリセンスで候補として表示され、 Action の中身も一応見れています。