Typescript stage mangement with quasar and vue not working

I am new to vuex and I am having trouble setting it up. My folder structure for store looks like this

  • store
  • module-example
    • index.ts
    • mutations.ts
    • getters.ts
    • state.ts
  • index.ts
  • store-flag.d.ts

The files looks as follows: index.ts

import { store } from 'quasar/wrappers';
import Vuex from 'vuex';

import example from './module-example';
// import { ExampleStateInterface } from './module-example/state';

/*
 * If not building with SSR mode, you can
 * directly export the Store instantiation
 */

export interface StateInterface {
  // Define your own store structure, using submodules if needed
  // example: ExampleStateInterface;
  // Declared as unknown to avoid linting issue. Best to strongly type as per the line above.
  example: unknown;
}

export default store(function ({ Vue }) {
  Vue.use(Vuex);

  const Store = new Vuex.Store<StateInterface>({
    modules: {
     example
    },

    // enable strict mode (adds overhead!)
    // for dev mode only
    strict: !!process.env.DEBUGGING
  });

  if (process.env.DEV && module.hot) {
    module.hot.accept(['./showcase'], () => {
      const newShowcase = require('./showcase').default
      Store.hotUpdate({ modules: { showcase: newShowcase } })
    })
  }

  return Store;
});

store-flag.d.ts

/* eslint-disable */
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
//  REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
import "quasar/dist/types/feature-flag";

declare module "quasar/dist/types/feature-flag" {
  interface QuasarFeatureFlags {
    store: true;
  }
}

index.ts

import { Module } from 'vuex';
import { StateInterface } from '../index';
import state, { ExampleStateInterface } from './state';
import getters from './getters';
import mutations from './mutations';

const exampleModule: Module<ExampleStateInterface, StateInterface> = {
  namespaced: true,
  getters,
  mutations,
  state
};

export default exampleModule;

mutations.ts

import { MutationTree } from 'vuex';
import state, { ExampleStateInterface } from './state';

const mutation: MutationTree<ExampleStateInterface> = {
  someMutation (state: ExampleStateInterface, token: ExampleStateInterface) {
    state = token
  }
};

export default mutation;

getters.ts

import { GetterTree } from 'vuex';
import { StateInterface } from '../index';
import { ExampleStateInterface } from './state';

const getters: GetterTree<ExampleStateInterface, StateInterface> = {
  getToken (state: ExampleStateInterface): string {
    return state.token
  },
  getUserName (state: ExampleStateInterface): string {
    return state.username
  },
  getRetrievalTime (state: ExampleStateInterface): Date {
    return state.retrievalTime
  }
};

export default getters;

state.ts

export interface ExampleStateInterface {
  token: string;
  username: string;
  retrievalTime: Date;
}

function state(): ExampleStateInterface {
  return { token:'dddddd', username: 'ddd', retrievalTime: new Date() }
};

export default state;

I am able to access the state like this

console.log(this.$store.state.example.retrievalTime)

However is returning an error due to any being of type any

However, I am unable to perform mutations. I tried this but nothing seems to happen.

this.$store.commit('example/someMutation', { token:'new', username: 'new', retrievalTime: new Date() })

I was unable to find any examples that works with quasar typescript online. Any suggestions are appreaciated.


This question is 8 months old, but I'll share my solution to work with Vuex in Quasar with TypeScript, with the new Composition API:

The Vue file/page is pretty simple and straight forward:

MyVuePage.vue:

<template>
  <q-page padding>
    <!-- content -->
    <p>This is the survival page.</p>
    <p>This is my counter: {{ counter }}</p>
    <q-btn color="primary" label="Increment" @click="addToCounter(3)" />
  </q-page>
</template>

<script>
import { defineComponent, computed } from 'vue';
import { useStore } from 'src/store';

export default defineComponent({
  name: 'Survival',
  setup() {
    const store = useStore();

    // store.getters['example/counter']
    // void store.commit('example/increment', val)

    return {
      counter: computed(() => store.state.example.counter),
      addToCounter: (val) => void store.dispatch('example/increment', val),
    };
  },
});
</script>

I modified the index.ts in the default quasar store folder to couple with TS types and also to have single store files instead of one for each call (getters, mutations, etc). To create a new store, add it to a subfolder called 'stores' and import it and it's interface as the 'example' below. Do not forget to export it's interface and as a module also, as seen:

store/index.ts

import { store } from 'quasar/wrappers';
import { InjectionKey } from 'vue';
import {
  createStore,
  Store as VuexStore,
  useStore as vuexUseStore,
} from 'vuex';

import example, { ExampleStateInterface } from './stores/example';

/*
 * If not building with SSR mode, you can directly export the Store instantiation;
 *
 * The function below can be async too; either use async/await or return a Promise which resolves
 * with the Store instance.
 */

export interface StateInterface {
  // Define your own store structure, using submodules if needed
  example: ExampleStateInterface;
  // Declared as unknown to avoid linting issue. example: unknown;
  // Best to strongly type as per the line above.
}

// provide typings for `this.$store`
declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $store: VuexStore<StateInterface>;
  }
}

// provide typings for `useStore` helper
export const storeKey: InjectionKey<VuexStore<StateInterface>> =
  Symbol('vuex-key');

export default store(function (/* { ssrContext } */) {
  const Store = createStore<StateInterface>({
    modules: {
      example,
    },

    // enable strict mode (adds overhead!)
    // for dev mode and --debug builds only
    strict: !!process.env.DEBUGGING,
  });

  return Store;
});

export function useStore() {
  return vuexUseStore(storeKey);
}

For this code to work, you just need to fix you store file in the 'stores' subfolder with a code like this:

store/stores/example.ts:

import { Module } from 'vuex';
import { GetterTree, MutationTree, ActionTree } from 'vuex';
import { StateInterface } from '../index';

export class ExampleStateInterface {
  counter = 1;
}

const state = new ExampleStateInterface();

const getters = <GetterTree<ExampleStateInterface, StateInterface>>{
  counter() {
    return state.counter;
  },
};

/* MUTATIONS TO COMMIT
  Always use a mutation to manipulate state. Nerver change state from an Action
  Mutations must be synchronous, but Actions can be async.
*/
const mutations = <MutationTree<ExampleStateInterface>>{
  increment(state, value) {
    state.counter += Number(value);
    // return state.counter;
  },
};

/* ACTIONS TO DISPATCH
  Always use a mutation to manipulate state. Nerver change state from an Action
  Mutations must be synchronous, but Actions can be async.
*/
const actions = <ActionTree<ExampleStateInterface, StateInterface>>{
  increment(context, value) {
    context.commit('increment', value);
  },
};

const exampleModule: Module<ExampleStateInterface, StateInterface> = {
  namespaced: true,
  state: state,
  getters: getters,
  mutations: mutations,
  actions: actions,
};

export default exampleModule;

This way you will have the new Vuex 4.x working in Quasar 2.4.. using the new amazing Composition API with types working all the way to your Vue page for state objects.