Как установить тип для объекта состояния в pinia?

Я делаю шахматы и использую Vue 3 и TypeScript с Pinia для управления состоянием.

Я хочу сделать что-то вроде следующего:

      export const useStore = defineStore("game", {
  state: () => {
    return {
      moves: [],
      gameBoard:  getInitialBoard(),
      playerTurn: PieceColor.White,
      previousPieceSelected: undefined
    }
  },
    updatePreviousPieceSelected(piece: Piece | undefined ) {
      this.previousPieceSelected = piece
    }
  }
})

UpdateGameState.vue

      setup() {
    const store = useStore()
    const previousPieceSelected: Piece | undefined = store.previousPieceSelected;
    let playerTurn: PieceColor = store.playerTurn;

    const initialGameState: GameState = {
      boardState: store.gameBoard,
      playerTurn,
    };

    const updateGameState = (
      cellRow: number,
      cellCol: number,
      currentPiece: Piece
    ) => {
      if (
        previousPieceSelected === undefined ||
        previousPieceSelected.pieceType === PieceType.None
      ) {
        store.updatePreviousPieceSelected(currentPiece);
      }
      if (
        (previousPieceSelected !== currentPiece && (currentPiece.pieceType === PieceType.None || currentPiece.color !== previousPieceSelected.color)) 
      ) {
        MovePiece(store.gameBoard, previousPieceSelected, {row: cellRow, col: cellCol} as Position)
        store.updatePreviousPieceSelected(undefined);
        store.changePlayer();
      }
    };

Однако я получаю сообщение об ошибке в следующей строке:

      store.updatePreviousPieceSelected(currentPiece);

Этот currentPiece (типа Piece) не может быть назначен типу undefined. Я нашел способ заставить это работать, выполнив в моем магазине следующее:

      export const useStore = defineStore("game", {
  state: () => {
    return {
      moves: [],
      gameBoard:  getInitialBoard(),
      playerTurn: PieceColor.White,
      previousPieceSelected: getInitialPreviousPieceSelected()
    }
  },
  actions: {
    changePlayer() {
      this.playerTurn =
          this.playerTurn === PieceColor.White
            ? PieceColor.Black
            : PieceColor.White;
    },
    updatePreviousPieceSelected(piece: Piece | undefined ) {
      this.previousPieceSelected = piece
    }
  }
})

function getInitialPreviousPieceSelected(): Piece | undefined {
  return undefined;
}

Но это чувствуется клуджем. Есть ли другой способ ввести previousPieceSelected в возвращаемое исходное состояние?

6 ответов

Решение

Тип this.previousPieceSelected выводится из начального состояния, и в настоящее время он инициализирован, поэтому он имеет тип (что означает, что ему может быть присвоено только значение).

  1. Используйте утверждение типа в начальном undefined значение (т. е. as ключевое слово плюс желаемый тип Piece | undefined).

  2. Также обратите внимание, что необязательные параметры могут быть указаны с помощью ?: вместо того | undefined. Это просто более простой способ его написания.

      export const useStore = defineStore("game", {
  state: () => {
    return {
      moves: [],
      previousPieceSelected: undefined as Piece | undefined, 1️⃣
    }
  },
  actions: {                          2️⃣
    updatePreviousPieceSelected(piece ?: Piece) {
      this.previousPieceSelected = piece
    }
  }
})

или просто так

      interface IUserState {
  user: null | IUser
}
    
export const useUserStore = defineStore({
  id: 'user',
  state: (): IUserState => ({
    user: null,
  })
...

defineStore— это общая функция в TypeScript, которая принимает два аргумента: идентификатор хранилища и состояние.

      /**
 * Creates a `useStore` function that retrieves the store instance
 *
 * @param id - id of the store (must be unique)
 * @param options - options to define the store
 */
export declare function defineStore<Id extends string, S extends StateTree = {}, G extends _GettersTree<S> = {}, A = {}>(id: Id, options: Omit<DefineStoreOptions<Id, S, G, A>, 'id'>): StoreDefinition<Id, S, G, A>;

Таким образом, можно определить полностью типизированный Stateи название магазина.

      import { defineStore } from 'pinia';

interface State {
  borks: string[];
  isWoof: boolean;
}

interface Getters {}

interface Actions {
  addBork(bork: string) => void;
}

const useBorkStore = defineStore<'bork', State, Getters, Actions>('bork', {
  state: () => {
    return {
      borks: [],
      isWoof: false
    }
  },

  actions: {
    addBork(bork: string) {
      this.borks.push(bork);
    }
  }
})

export { useBorkStore };

Если данные не загружены.

Попробуйте: https://pinia.vuejs.org/core-concepts/state.html#typescript

другой:

      import {defineStore} from 'pinia'

interface Menu {
  path: string
  name: string
  component: string
  meta: {
    title: string
    icon: string
    affix?: string
  }
}

const useAppStore = defineStore('app', {
  state: () => ({
    state: <boolean>false, // boolean
    message: <string>'msg', // string
    tagsView: <Menu[]>[] // array
  })
})
export default useAppStore

Например:

      type UserState = {
  login: string
  isPremium: boolean
}


const useUserStore = defineStore<string, UserState>('user', {
  state: () => ({
    login: 'test',
    isPremium: false,
  }),
})

Где getInitialBoard()приходящий из? Это импорт? ваши инициализированные данные не должны быть вашими вычисленными значениями. Это больше функция «добытчиков» в пинии. У вас есть «состояние», «геттеры» и «действия». Состояние обычно представляет собой инициализированные типизированные переменные. Геттеры — это вычисляемые возвращаемые значения без изменения данных состояния. Действия изменяют данные состояния. Вы должны изменять данные состояния только с помощью действий сохранения, а не внутри экземпляра компонента вне pinia. Все данные, источник правды, все время остаются в пинии. Держите свои магазины организованными таким образом:

      // conceptual model e.g.
pinia {
  state: () => {
    return { data }
  },
  getters: { 
    // doesn't change data
    getter: (state) => state.data.returnComputedState(),
  },
  actions: { 
    // changes data
    action: () => this.data = mutateTheData(this.data),
  },
}
Другие вопросы по тегам