網站搜尋

vue.js - 在 Vue3 和 TypeScript 中將 Pinia 儲存作為 Prop 傳遞


我正在使用 Typescript、Pinia 和 Vue3,並且有一個 MenuButton 組件,我希望能夠傳入一個 Pinia store,用於控制菜單的開啟狀態,以及顯示/隱藏的操作。應用中有幾個不同的菜單,因此我希望能夠將它們傳入,並且它們都使用相同的工廠來定義 store。我正在嘗試弄清楚如何讓這一切在 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>();

那麼使用方法相當簡單明了:

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

使用那個當前介面時,我收到了一個錯誤,說 props 不匹配。經過一些研究後,我將介面改為以下這樣,這解決了使用上的錯誤,但隨後在 MenuButton 組件中出現了錯誤,提示 toggle() 和 isOpen 未解析。

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

另一個接近成功的嘗試性調整是:

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

你的問題似乎主要是關於你需要的類型/介面,所以我將回答這個問題。

與其自己寫出一個類型,你可以直接使用返回的 store 的類型。

你有一個工廠函數,它返回 defineStore 的返回值,而 defineStore 本身是一個返回 store 的函數。因此,可以使用 typeof 來獲取工廠函數的類型,然後使用 TypeScript 的 ReturnType 輔助工具來深入獲取返回類型,以找到 store 的類型。

以下應該就是你所需要的全部:

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

細目:

ReturnType<                 // C
  ReturnType<               // B
    typeof menuStoreFactory // A
  >
>
  • A: 獲取工廠函數的類型。
  • B: 獲取工廠函數的返回類型,它是從 defineStore 返回的結果。這本身是一個函數,返回 store。
  • C: 取得該函數的回傳類型。這就是儲存本身的類型。