Mastering the art of typing Vuex modules in Vue 3 with TypeScript

I've been grappling with how to properly type vuex modules in a Vue 3 TypeScript project. The official documentation doesn't provide much guidance on this topic.

Let's say I have a setup like this:

import { createStore, useStore as baseUseStore, Store } from 'vuex';
import { InjectionKey } from 'vue';

interface FruitState  {
    apple: boolean,
    peach: boolean,
    plum: boolean
}

const FruitModule = {
    namespaced: true,
    state: (): FruitState => ({
      apple: true,
      peach: false,
      plum: true
    }),
    mutations: {},
    action: {}
}


export interface State {
    foo: string;
  }
  
  export const key: InjectionKey<Store<State>> = Symbol();
  
  export const store = createStore<State>({
      modules: {
        fruit: fruitModule
      },
      state: {foo: 'foo'},
      mutations: { 
        changeFoo(state: State, payload: string){
            state.foo = payload
        }
      },
      actions: { 
        setFooToBar({commit}){
         commit('changeFoo', 'bar')
      }}
  })

  export function useStoreTyped() {
    return baseUseStore(key);
  }
  

... and then later in a component:

  const apple = computed(() => store.state.fruit.apple);

However, attempting to access apple throws an error stating:

Property 'fruit' does not exist on type 'State'

If I make the following adjustments:

import { createStore, useStore as baseUseStore, Store } from 'vuex';
import { InjectionKey } from 'vue';

interface FruitState  {
    apple: boolean,
    peach: boolean,
    plum: boolean
}

const FruitModule = {
    namespaced: true,
    state: (): FruitState => ({
      apple: true,
      peach: false,
      plum: true,
    }),
    mutations: {},
    action: {}
}


export interface State {
    foo: string;
    fruit?: FruitState;
  }
  
  export const key: InjectionKey<Store<State>> = Symbol();
  
  export const store = createStore<State>({
      modules: {
        fruit: fruitModule
      },
      state: {foo: 'foo'},
      mutations: { 
        changeFoo(state: State, payload: string){
            state.foo = payload
        }
      },
      actions: { 
        setFooToBar({commit}){
         commit('changeFoo', 'bar')
      }}
  })

  export function useStoreTyped() {
    return baseUseStore(key);
  }

After making these changes, the error becomes Object is possibly 'undefined'

To work around this issue, I can use optional chaining like so:

const apple = computed(() => store.state.fruit?.apple);

However, I find this workaround unsatisfactory since I know that fruit.apple is never actually undefined.

Does anyone know the correct way to include a module in your state types for Vuex?

Answer №1

It is not necessary to make the fruit state optional within the State interface:

export interface State {
  foo: string;
  //fruit?: FruitState; 
  fruit: FruitState;
}

Your goal was to address a TypeScript error when defining the root state (shown here):

export const store = createStore<State>({
  modules: {
    fruit: fruitModule
  },
  state: { foo: 'foo' }, // ❌ Property 'fruit' is missing in type '{ foo: string; }' but required in type 'State'.
})

To resolve this, you can utilize type assertion as a workaround:

export const store = createStore<State>({
  modules: {
    fruit: fruitModule
  },
  state: { foo: 'foo' } as State, // ✅
})

Check out the demo here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using JavaScript, import the variable module object from a specific module

Is there a way to import a module object from a module if I am unsure of the specific objects I will need beforehand? How can I utilize a variable in place of fixed module names? import { dynamicVariable } from './module' The variable represents ...

Using JQuery to Load a CSHTML File into a Modal in C#

I am attempting to have the content of one specific page loaded into a modal on a different page when a button is clicked. Unfortunately, I am encountering an issue where not only the desired content from the specified page is loading, but also content fro ...

Every individual child component must be assigned a distinct key prop, even if they are pre-defined. - Utilizing REACT

My navigation bar routes are sourced from a JSON file structured like this: { "categorias": [ { "nombre": "Teacher absences", "componentes": [ { "type": "url", ...

Replicating radio button functionality using buttons in an HTML document

I am attempting to simulate radio button behavior using normal buttons for a quiz application. Currently, my code is working as intended and permanently highlights buttons with the desired color. However, I want all other buttons in a question to be white, ...

Material-UI organizes its Grid Items vertically, creating a column layout rather than a horizontal row

I'm struggling to create a grid with 3 items in each row, but my current grid layout only displays one item per row. Each grid item also contains an image. How can I modify the code to achieve a grid with 3 items in a row? Here's the code snippet ...

Using play() in HTML Audio prevents the IOS music player from playing

When developing a timer app, I am currently using html audio to play a beep sound by utilizing the audioElement.play() function. The user must interact with the screen to enable sound (press the unmute button). This particular web application acts as a fit ...

Enhanced dropdown menu with Vue.js

Trying to manage two select lists where their combined values equal a maximum size. For example, if the max number of people is 20 and 5 children are selected, only a maximum of 15 adults can be added, and so on. My latest attempt: Template: <div cla ...

Implement a delay for a specific function and try again if the delay expires

In my TypeScript code, I am utilizing two fetch calls - one to retrieve an access token and the other to make the actual API call. I am looking to implement a 1-second timeout for the second API call. In the event of a timeout, a retry should be attempted ...

Tips for modifying JSON property names during the parsing process

As outlined in the JSON.parse documentation, a reviver function can be utilized to modify the value of each property within the JSON data. Here is an example: JSON.parse('{"FirstNum": 1, "SecondNum": 2, "ThirdNum": 3}', function(k, v) { return ...

Firebase console does not show any console.log output for TypeScript cloud functions

I encountered an issue while using Typescript to write some Firebase cloud functions. Here is a snippet of my code: index.ts export * from "./Module1"; Module1.ts import * as functions from "firebase-functions"; export const test = functions.https.onR ...

Ensure the main page occupies the entire screen without the need for scrolling

Can someone help me with my Vue app? I need to figure out how to make the start page full screen without any scroll bars in the main window at any scale. The scrollbar should be located within "div class="bottom-center". <script setup> import Comp fr ...

Learn how to generate a DataTable in C# by parsing multiple nested JSON Objects efficiently

Whenever I receive dynamic JSON data, it could be a JSON array, a simple JSON object, or nested JSON objects. I am attempting to deserialize and convert the JSON object/array into a DataTable for further processing using Newtonsoft.Json. Here is my curre ...

Ways to enlarge image size without compromising the image resolution?

My image is in PNG format or as a blob. It has dimensions of 800px by 600px. However, when I try to resize it using various canvas methods like the one mentioned in this Stack Overflow thread: Resize image, need a good library, it loses quality. I wou ...

The ordering of my styles and Material-UI styles is causing conflicts and overrides

Greetings fellow developers! I'm currently facing an issue with my custom styles created using makeStyles(...). The problem arises when I import my styles constant from another module, and the order of the style block is causing my styles to be overr ...

Enhance information flow within pages using SWR in NextJS

Utilizing SWR in a NextJS project has been a great experience for me. I have successfully fetched a list of data on my index page and added a new entry to the data on my create page. Now, I am looking to take advantage of SWR's mutate feature to updat ...

Having trouble clearing the interval after setting it?

I developed a slideshow script called function slide(). The intention is for this function to begin when the 'play' button is clicked and pause when the 'pause' button is clicked. I implemented setinterval and it functions properly, how ...

Prevent the bottom row from being sorted

I have implemented sortable table rows in my angular project, however the sorting functionality also affects some query-ui code elements. Now I am looking to exclude the last row from being sortable. HTML <div ng:controller="controller"> <ta ...

What is the best method for choosing elements when I already possess a DOM element?

When you have a variable that contains a DOM element and need to select related elements, you can easily achieve this by wrapping it in a jQuery object. var myDomElement = document.getElementById("foo"); $(myDomElement ).find("a"); It is common for peopl ...

What is the best way to synchronize the state of a component with the updated value in a Vuex store?

Within my Vuex store, I have implemented mutations that receive a message from one component and are responsible for showing/hiding a prompt message in another component (similar to displaying a "You are logged in" prompt after a successful login): setPro ...

The sudden appearance of the "export" token during the execution of this code has

Currently, I am utilizing nodejs version 10.13.0. When attempting to run this code through terminal commands node --experimental-modules main.mjs, an error is encountered: (node:3418) ExperimentalWarning: The ESM module loader is experimental. file:///hom ...