You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3+TypeScript下如何将Pinia Store作为组件Prop传递

Vue3 + Pinia + TypeScript 菜单组件Store类型适配问题

我使用TypeScript、Pinia和Vue3开发项目,现有一个MenuButton组件,需要将管理菜单开闭状态及显示/隐藏动作的Pinia Store作为Prop传入。应用内多个菜单均通过同一工厂函数定义Store,目前正解决TypeScript类型适配问题。


初始代码与类型不匹配错误

Store定义代码(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');
import { MenuIcon, MenuLeftIcon } from "@/icons";
import type { MenuStore } from "@/modules/nav/nav.store";

interface Props {
    controller: MenuStore
}

const props = defineProps<Props>();

组件使用方式

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

此时出现Prop类型不匹配错误,原因是Pinia生成的Store实例包含额外内置属性,和自定义的MenuStore接口不兼容。


尝试继承PiniaCustomStateProperties的问题

修改MenuStore接口继承PiniaCustomStateProperties:

import type { PiniaCustomStateProperties } from 'pinia';

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

这种修改解决了组件使用时的类型错误,但组件内部访问props.controller.toggle()或props.controller.isOpen时,会提示toggle()和isOpen未定义。


尝试继承Store接口的报错

修改MenuStore接口继承Pinia的Store接口:

import type { Store } from 'pinia';

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

此时组件内部无类型错误,但使用组件时出现类型不兼容报错:

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


正确解决方案

步骤1:拆分State和Actions类型

在nav.store.ts中单独定义State和Actions类型,明确边界:

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

// 菜单状态类型
interface MenuState {
  isOpen: RemovableRef<boolean>;
}

// 菜单动作类型
interface MenuActions {
  toggle(force?: boolean): void;
  open(): void;
  close(): void;
}

// 通用MenuStore类型:Pinia Store实例,包含指定状态和动作
export type MenuStore = Store<string, MenuState, {}, MenuActions>;

步骤2:修正工厂函数返回类型

让工厂函数返回的defineStore结果明确对应MenuStore类型:

function menuStoreFactory(id: string) {
  return defineStore(id, {
    state: (): MenuState => ({
      isOpen: useStorage(`${id}-open`, false),
    }),
    actions: {
      toggle(force?: boolean) {
        this.isOpen = force !== undefined ? force : !this.isOpen;
      },
      open() {
        this.isOpen = true;
      },
      close() {
        this.isOpen = false;
      },
    },
  }) as () => MenuStore;
}

export const useMainMenuStore = menuStoreFactory('mainMenu');
export const useMobileMenuStore = menuStoreFactory('mobileMenu');

步骤3:组件内使用修正后的类型

MenuButton组件的Props定义保持不变,现在类型可完全匹配:

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

interface Props {
    controller: MenuStore
}

const props = defineProps<Props>();

修改后,组件内部调用Store方法/访问状态、传递Store实例给组件时,均不会出现类型错误。


内容的提问来源于stack exchange,提问作者aron.duby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:25:19