Strong typed Pinia stores in Vue

Strong typed Pinia stores in Vue

In this Article

Overview

If you haven’t had your hands on Pinia I recommend you to check it out. It comes with types support out of the box on top of that I always like to be very verbose when coding in a strong typed language like typescript. In this article I will show an example of how I have typed a pinia store so that I get typed safety when using the store in a vue component.

Type the store

In my example I am going to create an authentication store separating the implementation from getters, actions and types definition in a folder named authentication

Let’s start by defining the types.ts

/**
 * Defines the state structure of the authentication store
 */
export interface AuthenticationStoreState {
  /**
   * The authentication token
   */
  token: string | null
}

/**
 * Defines the authentication store getters types
 */
export type AuthenticationStoreGetters = {
  isAuthenticated: (state: AuthenticationStoreState) => boolean
}

/**
 * Defines the authentication store as a strong type store
 */
export type AuthenticationStore = ReturnType<typeof import('./authentication.store')['useAuthenticationStore']>

In the file above we are defining the getters types as well as the store and store states. Note that for the AuthenticationStore type we are saying the the type is the same as the return type from the function imported from the authentication.store.ts file. We’ll get back to that in a moment. Now let’s look at the actions.ts file:

import { AuthenticationStoreState } from "./types"

/**
 * Set token action implementation
 * 
 * @param state the authentication store state to set the token value for
 * @param token the token value to set to the store state
 * @returns the set token value
 */
 export const setToken = (state: AuthenticationStoreState, token: string | null): string | null => state.token = token

The snippet above is pretty straightforward setToken is a function that allows to set the token to the store state. Let’s now look at the getters.ts:

import { AuthenticationStoreState } from "./types"

/**
 * Checks whether the user is authenticated or not
 * 
 * @param state the authentication store state 
 * @returns true when the state token is not null or false otherwise
 */
 export const isAuthenticated = (state: AuthenticationStoreState): boolean => {
  return state.token !== null
}

This getters checks whether the token is set or not and returns a boolean. Let’s tie it all together in the authentication.store.ts file:

import { defineStore } from 'pinia'
import { setToken } from './actions';
import { isAuthenticated } from './getters';
import { AuthenticationStoreGetters, AuthenticationStoreState } from './types';

// init the authentication store state with a null token
const state = (): AuthenticationStoreState => ({
  token: null,
})

// define the getters
const getters: AuthenticationStoreGetters = {
  isAuthenticated,
}

// define the actions
const actions = {
  setToken,
}

// define the store
export const useAuthenticationStore = defineStore('authentication', {
  state,
  getters: {...getters},
  actions
})

Here we have finally defined and typed the authentication store. This is all we needed to do to type safe our authentication store.

Use the store in a vue component

Let’s see this in action in a vue file:

<script setup lang="ts">
import { ref, Ref } from 'vue'
import { useAuthenticationStore } from '@/stores/authentication/authentication.store'
import { AuthenticationStore } from '@/stores/authentication/types';

// get the authentication store instance
const authStore: AuthenticationStore = useAuthenticationStore()

const test: Ref<string> = ref('')

// call the getter with type safety
if (authStore.isAuthenticated) {
  test.value = 'logged in'
} else {
  test.value =' logged out'
}

</script>
<template>
  <p>...</p>
</template>


Now you can cmd hover on the object or any of its properties and see type safety in action.