What is the proper way to define the type of the state object in pinia?

I am currently working on a chess game project using Vue 3 and TypeScript along with Pinia for state management.

Here is an example of what I want to implement:

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();
      }
    };

However, I encountered an error on the following line:

store.updatePreviousPieceSelected(currentPiece);

The error states that currentPiece (of type Piece) is not assignable to type undefined. As a workaround, I made some changes in my store like so:

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;
}

Even though it works, it feels somewhat cumbersome. Is there a better way to type previousPieceSelected in the initial state return?

Answer №1

The data type of this.previousPieceSelected is automatically determined by the initial state and is currently set to undefined, meaning its type is also undefined (which restricts it from being assigned any other value).

  1. To specify a different type for the initial undefined value, you can utilize a type assertion (using the as keyword followed by the desired type like Piece | undefined).

  2. Additionally, remember that optional parameters can be denoted with ?: instead of | undefined, offering a more concise notation.

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

Answer №2

or maybe even similar

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

Answer №3

defineStore is a versatile function found in TypeScript that takes in two parameters: the store ID and the state.

/**
 * This creates a `useStore` function to access the store instance
 *
 * @param id - unique identifier for the store
 * @param options - options for defining 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>;

This allows for the precise definition of typed State and the name of the store.

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 };

Answer №4

If the information isn't loading properly.

Check out:

Alternatively, you can try:

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

Answer №5

Let's take a look at this illustration:

interface UserDetails {
  name: string
  age: number
}

const useUserInformation = defineStore<string, UserDetails>('user', {
  state: () => ({
    name: 'John Doe',
    age: 30,
  }),
})

Answer №6

Can you clarify the origin of the function getInitialBoard()? Is it being imported from another source? It's important to keep in mind that your initialized data should not be directly tied to your computed values. This responsibility falls more on the "getters" in pinia. In pinia, there are "state", "getters", and "actions". State typically consists of initialized, typed variables. Getters compute returnable values without modifying the state data. Actions are responsible for altering the state data. It's best practice to only modify the state data using store actions within pinia, rather than making changes directly within the component instance outside of pinia. Remember, all the data, which is considered the source of truth, should remain within pinia at all times. When structuring your stores, follow this organization:

// Example conceptual model
pinia {
  state: () => {
    return { data }
  },
  getters: { 
    // does not alter data
    getter: (state) => state.data.returnComputedState(),
  },
  actions: { 
    // modifies data
    action: () => this.data = mutateTheData(this.data),
  },
}

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

Employing the keyof operator with the typeof keyword to access an object

What data type should be used for the key variable? I am encountering an error stating that "string" cannot be used to index the type "active1: boolean, active2". const [actives, setActives] = React.useState({ active1: false, active2: false, }); con ...

Ensure that Vue.js Accordion items do not auto-close when a different item is selected

I'm looking to create an Accordion menu using vuejs. The requirement is for the Accordion to stay open even if another div is clicked, and to only close when the Accordion item itself is clicked. How can I achieve this? Here is the vue code: new Vue( ...

Exploring nested objects with ReactJS and mapping through them

After receiving an object from the backend, I am loading it into a Redux property inside my Component called user. The schema of this object is as follows: { user: 'root', perm: [ "admin", "write", "read", "nodata ...

What's new with event handling in Vue 3.0?

Looking for a way to handle an event in a child component, check for a specific condition, and then emit the "submit" event back to the parent for its event handler to run. The code snippet below demonstrates this process using Vue.js 2.6.11 (replacing "vu ...

What is the best way to connect an input to an array using Vuetify and Vue.js?

I've recently integrated Vuetify.js into my project and it's been a smooth process so far. One thing I'm currently working on is adding custom error messages to the username input field. Specifically, I want to display different messages dep ...

MyApp is encountering issues resolving all parameters

I'm encountering an issue that none of the other similar questions have been able to help me solve. Can anyone offer assistance? I've already attempted removing parameters one by one, but I'm still stuck. Can't resolve all parameters f ...

Efficiently Updating Property Values in Objects Using TypeScript and Loops

I have been looking into how to iterate or loop through a code snippet, and while I managed to do that, I am facing an issue where I cannot change the value of a property. Here is the snippet of code: export interface BaseOnTotalPaidFields { groupName ...

Using the mapState function in vuex allows for easy access to Vue methods

I have an important task to complete while the data is being computed using vuex mapState. I must ensure that the vue method countAlerts is called every time the data changes; however, the computed property needs to invoke this method and unfortunately, ...

Leverage the power of mathematical functions within Angular to convert numbers into integers

In my Angular 7 Typescript class, I have the following setup: export class Paging { itemCount: number; pageCount: number; pageNumber: number; pageSize: number; constructor(pageNumber: number, pageSize: number, itemCount: number) { thi ...

Using TypeScript to create a generic type that wraps around HTMLElements

I attempted to execute this action, however the assignment to this.element is not working and I am unsure why. class Elem<T> { public element : T; constructor(typeElement:string){ this.element = document.createElement(typeElement); ...

Is polymer routing the best choice for large-scale websites?

I am currently working on a large project that consists of various layouts and structures, totaling around 100 different pages. Despite the variations in content, the core elements such as headers and menus remain consistent throughout. To streamline my ...

Cannot send response headers once they have already been sent to the client [NEXTJS]

Currently, I am engrossed in a personal project focused on creating a dashboard using NextJS. This project serves as an opportunity for me to delve into learning NextJS and the fundamental concepts of TypeScript. My primary challenge at the moment revolves ...

Attributes could potentially be null

I created a small Vue functional component that receives a prop from its parent: export default defineComponent({ name: 'ExpandedMovieInformation', props: { movie: { type: Object as PropType<Movie>, }, }, setup(props, ...

Containerizing Vue with Flask using uwsgi and nginx

I have a client-server project that I'm looking to containerize. The project structure looks like this: ├── client │ ├── dist │ ├── node_modules │ ├── public │ └── src ├── nginx └── server ...

Limiting the table width in TinyMCE

Currently, I am utilizing TinyMCE within a Vue application. I've set up TinyMCE with the table_grid: false configuration, causing a dialog to appear when a table is created. This dialog allows users to specify rows, columns, width, and more. Within t ...

Vue does not recognize the Nuxt $route

Greetings, I've encountered a strange issue. I'm working on a Nuxt app with Typescript. In the created hook, I am using console.log to log this.$route. The log is functioning correctly and I am able to read the params from the route. However, d ...

Slate - developing a TypeScript function to filter and retrieve an object containing the highest property value

Check out this NEW RELATED QUESTION: I need to extract the largest number from a given object set. I am struggling with finding a solution. I have tried using max but I think my skills are lacking. Here is the code I have so far: @Function() pub ...

"Utilizing the `useState` function within a `Pressable

Experiencing some unusual behavior that I can't quite figure out. I have a basic form with a submit button, and as I type into the input boxes, I can see the state updating correctly. However, when I click the button, it seems to come out as reset. Th ...

What could be causing the issue with axios when trying to send a PUT request for a Laravel API call?

Need help with my Vue component request: submit() { axios .put(`/api/posts/${this.slug}`, this.fields, { headers: { "content-type": "multipart/form-data" }, }) .then((res) => { // s ...

What is the most effective method for defining 2 routes that point to the same component?

Although it may seem straightforward, I'm struggling to find the most efficient method for this particular scenario in Vue.js. I am using Vue Cli 3 and I need to have multiple routes leading to the same Home page within the application. The idea is ...