12

I followed this tutorial to set up a Vuex store with modules using TypeScript.

So far I have:

vuex/types.ts:

export interface RootState {
    version: string;
}

vuex/user-profile.ts:

import { ActionTree, Module, MutationTree } from 'vuex';
import { RootState } from './types';

interface User {
    firstName: string;
    uid: string;
}

interface ProfileState {
    user?: User;
    authed: boolean;
}

const state: ProfileState = {
    user: undefined,
    authed: false,
};

const namespaced: boolean = true;

export const UserProfile: Module<ProfileState, RootState> = {
    namespaced,
    state,
};

store.ts:

import Vue from 'vue';
import Vuex, { StoreOptions } from 'vuex';
import { UserProfile } from '@/vuex/user-profile';
import { RootState } from '@/vuex/types';

Vue.use(Vuex);

const store: StoreOptions<RootState> = {
  state: {
      version: '1.0.0',
  },
  modules: {
      UserProfile,
  },
};

export default new Vuex.Store<RootState>(store);

In my router.ts I want to access the authed state of the store like this:

import store from './store';
//...other imports...

const router = new Router({
//... route definitions...
});

router.beforeEach((to, from, next) => {
  const isAuthed = store.state.UserProfile.authed;
  if (to.name !== 'login' && !isAuthed) {
    next({ name: 'login' });
  } else {
    next();
  }
});

The code works (the app redirects properly), HOWEVER, the compiler throws errors saying Property 'UserProfile' does not exist on type 'RootState', which makes sense since it's not defined, but should it not look under the modules as well, or did I not define the module correctly?

Andre12
  • 554
  • 1
  • 7
  • 17
  • Have you tried adding the UserProfile in the RootState interface ? – Va54 Dec 26 '22 at 09:02
  • There is a alternative way to get the Module in the Vuex, you can get it using the getModule method from the "vuex-module-decorators" lib – Va54 Dec 26 '22 at 09:03

5 Answers5

1

You need to define all stores to rootState interface like this:

export default interface RootState {
  version: string,
  UserProfile: any
}

You could also import the interface from the UserProfile and use instead of any. Since you dont use pascal casing for your file names

This will tell the rootState to expect a vuex store called UserProfile with any type or UserProfile interface.

I have a rootstate interface for my vuex store like this:

export default interface RootState {
  version: string,
  config: any,
  cart: any,
  checkout: any,
  cms: any,
  product: any,
  shipping: any,
  user: any,
  wishlist: any,
  attribute: any,
  ui: any,
  newsletter: any,
  category: {
    current_path: string,
    current_product_query: any,
    current: {
      slug: string,
      name: string
    },
    filters: any
  }
}
Lervad
  • 106
  • 6
-1

EDIT: It seems that direct access to state is the issue here. Line

const isAuthed = store.state.UserProfile.authed;

I believe this happens because it's namespaced. The solution would be to create a getter.

const getters: GetterTree<ProfileState, RootState> = {

    user(state): User {
        return state.user
    }

};

And then you can access it like

store.getters['UserProfile/user']

Also, please consider using getters for accessing your state data. See Getters for reference.

Danijel
  • 1,145
  • 8
  • 15
  • 1
    I do have RootState in my module: `export const UserProfile: Module ` – Andre12 Oct 19 '18 at 13:05
  • Yes, but did you add GetterTree, MutationTree etc? I believe those should also be added and exported even when empty. – Danijel Oct 19 '18 at 13:08
  • I added all the code (getters, mutations, actions, etc. etc.) same issue – Andre12 Oct 24 '18 at 21:53
  • I edited the answer. Could you please also try exporting the main store empty mutations, actions, and modules at the end? I vaguely remember coming across this issue and solved it like that at the beginning. – Danijel Oct 25 '18 at 06:19
  • i did, same issue persists – Andre12 Oct 25 '18 at 21:27
  • This is not how getters are supposed to be used, store references are already reactive. – Etheryte Mar 18 '19 at 21:49
-1

1

     const isAuthed = store.state["UserProfile"].authed; // false

2

    const state:any|State = store.state
    const isAuthed = state.UserProfile.authed; // false

3

    const isAuthed = (<any|State>store.state).UserProfile.authed; // false
Wyf
  • 7
  • 1
  • 1
    1 has the same problem. 2 and 3 work, but casting them as `any` sort of negates the purpose of typescript. upvoted. – Andre12 Apr 04 '19 at 13:37
-1

This is a working solution with dependecies "vuex": "^4.0.2", "vue-router": "^4.0.10" and "vue": "^3.1.4".

Import the store into your router file to access your state in the module:

import store from "@/store/store";

In my case the modules name is authModule and I access the state of token, where a jwt is stored:

let loggedIn = store.state.authModule.token;
StevenSiebert
  • 1,306
  • 4
  • 15
  • 26
  • Your answer is missing the key information: how did you type `store` so that you can access `store.state.authModule`? – kleinfreund Aug 15 '22 at 12:26
  • It is not a trick question. You demonstrate how you import `store` and how you access it, but you don’t show what type you assigned to `store`. This is crucial in order to answer this question because that is the problem: how to type a store so that it allows access of module state without error. Also, vuex does not provide `store` as you import it. – kleinfreund Aug 16 '22 at 19:48
  • @kleinfreund Sorry, but just asking "how did you type `store` so that you can access ..." show missing knowledge. You don't "type" it in a special way and it was not necessary to explain how to import a module in `vue.js`, as the questioning user according to "import store from './store';" already know how to do it. This is not part of the question. – StevenSiebert Aug 18 '22 at 17:21
-1

I would recommend doing a double cast. One to any and one back to what you actually want. Remember that in the last router.ts file below, "store" is an instance of the Store and the other two imports are just types.

I have omitted the namespaced, state, getters, actions, mutations code for brevity. They are simply object structures

store/myModule/types.ts:

export default interface State {
    testValue
}

store/myModule/index.ts:

import RootState from '../types'

const module: Module<State, RootState> = {
    namespaced,
    state,
    getters,
    actions,
    mutations,
}

export default module

store/types.ts:

interface RootState {
  myOtherTestValue: string
}

export default RootState
export { RootState }

store/index.ts:

import RootState from './types'
import myModule from './myModule'

export const cmStore: StoreOptions<RootState> = {
    actions,
    mutations,
    state,
    modules: {
        myModule,
    },
    plugins,

}

export default new Vuex.Store<RootState>(cmStore)

in router.ts:

import store from './store'
import RootState from './store/types'
import MyModuleState from './store/myModule/types'

// to get what you want typed how you want:
const myState = ((store.state as any).myModule as MyModuleState)

console.log(myState.testValue)
Frank
  • 1
  • 1