Utilizing TypeScript with Vue3 to Pass a Pinia Store as a Prop

My current stack includes Typescript, Pinia, and Vue3. I have a MenuButton component that I want to be able to pass a Pinia store for managing the menu open state and related actions. There are multiple menus in the application, each using the same store factory method to define the stores. I am trying to make this setup work seamlessly with Typescript.

// nav.store.ts

import { defineStore } from "pinia";
import { useStorage } from "@vueuse/core";
import type { RemovableRef } from "@vueuse/core";

export interface MenuStore {
    isOpen: RemovableRef<boolean>,
    toggle(force?: boolean) : void,
    open(): void,
    close(): void,
}

interface State {
    isOpen: RemovableRef<boolean>;
}

function menuStoreFactory(id: string) {
    return defineStore(id, {
        state: () : State => ({
            isOpen: useStorage(`${id}-open`, false),
        }),

        actions: {
            toggle(force?: boolean) {
                this.isOpen = force != undefined ? force : !this.isOpen;
            },

            open() {
                this.isOpen = true;
            },

            close() {
                this.isOpen = false;
            }
        }
    });
}

export const useMainMenuStore = menuStoreFactory('mainMenu');

export const useMobileMenuStore = menuStoreFactory('mobileMenu');
// setup script for the menu button component

import { MenuIcon, MenuLeftIcon } from "@/icons";
import type { MenuStore } from "@/modules/nav/nav.store";

interface Props {
    controller: MenuStore
}

const props = defineProps<Props>();

Using the store is straightforward:

<template>
    <MenuButton
        :controller="mainMenu"
    ></MenuButton>
</template>
<script setup lang=ts">
    const mainMenu = useMainMenuStore();
</script>

Initially, there was an error regarding mismatched props. After tweaking the interface as follows, the usage error was fixed, but then another error occurred in the MenuButton component indicating that toggle() and isOpen were unresolved.

export interface MenuStore extends PiniaCustomStateProperties<{
    isOpen: RemovableRef<boolean>,
    toggle(force?: boolean) : void,
    open(): void,
    close(): void,
}> {}

Another attempt involved the following tweak:

export interface MenuStore extends Store<string, {
    isOpen: RemovableRef<boolean>,
    toggle(force?: boolean) : void,
    open(): void,
    close(): void,
}> {}

This resulted in an error upon usage, though no error in the component itself.

Type _StoreWithState<string, State, {}, {toggle(force?: boolean): void, close(): void, open(): void}> & UnwrapRef<State> & _StoreWithGetters<{}> & {toggle(force?: boolean): void, close(): void, open(): void} & PiniaCustomProperties<string, State, {}, {toggle(force?: boolean): void, close(): void, open(): void}> & PiniaCustomStateProperties<State> is not assignable to type MenuStore ...   Type PiniaCustomStateProperties<State> is not assignable to type MenuStore 

Answer №1

If you're wondering about the type or interface needed for your question, I've got you covered.

You don't have to manually write out a type - just use the type of the store that is returned.

By using a factory function that returns the value from defineStore, which in turn returns the store, you can determine the type of the store by utilizing typeof and TypeScript's ReturnType.

All you need is this simple line:

export type MenuStore = ReturnType<ReturnType<typeof menuStoreFactory>>

Here's the breakdown:

ReturnType<                 // C
  ReturnType<               // B
    typeof menuStoreFactory // A
  >
>
  • A: Obtain the type of the factory function.
  • B: Retrieve the return type of the factory function, which is the result of defineStore. This function itself returns the store.
  • C: Obtain the return type of that particular function, representing the store's type.

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

Mastering Angular 2 Reactive Forms: Efficiently Binding Entire Objects in a Single Stroke

Exploring reactive forms in Angular 2 has led me to ponder the possibility of binding all object properties simultaneously. Most tutorials show the following approach: this.form = this.fb.group({ name: ['', Validators.required], event: t ...

Tips for choosing and filtering the preferred object in ES6

Consider this array structure: const testData = [ { group: "Team1", info: [ { key: 123, person: "Alice", type: "Football" }, { key: 456, person: "Bob", type: " ...

What steps should be followed to effectively incorporate Google Fonts into a Material UI custom theme for typography in a React/TypeScript project

Hey there, I'm currently working on incorporating Google fonts into a custom Material UI theme as the global font. However, I'm facing an issue where the font-weight setting is not being applied. It seems to only display the normal weight of 400. ...

Navigating with Vue Router on Internet Information Services (IIS)

I am encountering difficulties understanding why my routes are failing when I refresh my VueJS application hosted on IIS. Everything works fine during normal browsing, but once I press F5 or update view information through a button, a 403 error is thrown. ...

Can one validate a single route parameter on its own?

Imagine a scenario where the route is structured as follows: companies/{companyId}/departments/{departmentId}/employees How can we validate each of the resource ids (companyId, departmentId) separately? I attempted the following approach, but unfortunate ...

implement a solution in Ionic 4 Angular 6 that triggers a function only when all the observables in a loop have

In my code, I have a function named getDevices(). This function is responsible for fetching an array of devices. After getting this array, the code iterates through each device and calls another function called updateToServer(). The purpose of updateToServ ...

The Vue.js component initializes without waiting for the axios.get() request to complete

One of my Vue components includes a prop named objects: <Table :objects="orders"/> When using axios.get() in the mounted() lifecycle hook to retrieve data from Directus CMS, the code looks like this: this.orders = await this.axios.get(&apo ...

encountered an issue while accessing a FastAPI-based API

While developing a login feature in Next.js, I encountered an issue when making a request to an API created in FastAPI to retrieve a cookie. The problem arises during the fetch operation on the frontend specifically when setting credentials to 'includ ...

Adjusting the width of the xAxis in Vuejs with Highcharts

I'm working on creating a bar graph with HighchartJS. Everything works perfectly when the start date is set to 1st Jan. However, when I dynamically change the data to start from 2nd Jan, the grouped Bar chart doesn't display properly. It seems th ...

The Nuxt3 application is experiencing technical issues when running in production

My app performs well in development mode when using "npm run dev". However, once I build it with "npm run build" and then launch it with "npm run start", it starts to malfunction. Specifically, the dynamic styles that control whether a button should be d ...

Enhance Express Middleware with Typescript for advanced functionality

Currently in the process of developing a REST API using Express and Typescript, I am encountering difficulties when trying to extend the Request/Response objects of Express. Although my IDE shows no errors, Typescript throws TS2339 Errors during compilati ...

Ways to remove a vuejs component externally

I have constructed a Vue.js component like this: var tree_data = Vue.extend({ template: '#tree_data_template', props: [ 'groupmodal', 'search-name', 'tree-id' ], data: functio ...

organize column and row indexes in vuejs by descending order

I have created a table with rows and columns. Right now, the cell located at (1,1) is in the top-left corner of the table. Is there a way to change it so that the count starts from the bottom left instead? This is the code I am currently using: <tabl ...

The issue of event emission not being registered for custom filters in vue-tables-2

I keep encountering this issue: "TypeError: Event.$emit is not a function" every time the watcher triggers. Even though I imported the Event object in my main.js file and can log it to the console, I am using a named prop due to having three different cli ...

There seems to be a problem with the Chart property getter as it

I'm in the process of creating an object that corresponds to chartJS's line-chart model <line-chart :data="{'2017-05-13': 2, '2017-05-14': 5}"></line-chart> There is an API I utilize which returns a standard arra ...

Having trouble retrieving data from the payload within vuex actions

I am currently utilizing Vuex for state management. Here is my store.js file: import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex); export const store = new Vuex.Store({ state: { loggedIn : false }, getters: {}, mutations: ...

Is there a way to easily toggle a Material Checkbox in Angular with just one click?

Issue with Checkbox Functionality: In a Material Dialog Component, I have implemented several Material Checkboxes to serve as column filters for a table: <h1 mat-dialog-title>Filter</h1> <div mat-dialog-content> <ng-container *ng ...

What is the best way to update a data value in one Vue Js component and have it reflected in another component?

I'm a newcomer to Vue Js and encountering an issue with changing data values from another component. Let's start with Component A: <template> <div id="app"> <p v-on:click="test ()">Something</p> </div> ...

How can I dynamically insert various FormGroup instances into a FormArray in Angular?

I am looking to dynamically populate the order array with multiple dishes. Each dish will be stored as a FormGroup in the form, such as pizza, salad, drink, or any other type of dish. Prior to adding any items, the form structure should resemble this: this ...

Exploring Object Iteration in Tabs with VueJS

I need assistance with iterating through an object instead of an array in my tabs. How can I achieve this? Here is the code snippet: <v-tabs v-model="tab" background-color="transparent" color="basil" grow> <v-tab ...