Pinia является официальным state management решением для Vue.js и обеспечивает удобный, реактивный и типобезопасный способ управления состоянием приложения. Одним из ключевых аспектов современного использования Pinia является типизация store, что особенно важно при работе с TypeScript. Типизация позволяет получить автодополнение, проверку типов во время компиляции и минимизировать ошибки, связанные с некорректным использованием состояния или действий.
Для создания типизированного store в Pinia используется функция
defineStore. Основной подход заключается в том, чтобы явно
описывать интерфейс состояния и типы геттеров и действий.
import { defineStore } from 'pinia'
interface UserState {
id: number
name: string
isAdmin: boolean
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
id: 0,
name: '',
isAdmin: false,
}),
getters: {
welcomeMessage: (state): string => `Добро пожаловать, ${state.name}!`
},
actions: {
setName(newName: string) {
this.name = newName
},
promoteToAdmin() {
this.isAdmin = true
}
}
})
Ключевые моменты:
state возвращает объект с типом UserState.
Тип указывается явно через (): UserState.state), так и для возвращаемого значения.При подключении store к компоненту Vue типизация сохраняется и предоставляет полное автодополнение.
<script lang="ts" setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
userStore.setName('Иван')
console.log(userStore.welcomeMessage)
</script>
Преимущества:
ref и
computed без потери типизации.Для более сложных геттеров можно использовать возвращаемые типы через
ReturnType или явное указание типа:
getters: {
fullName(state): string {
return `${state.name} (${state.isAdmin ? 'Админ' : 'Пользователь'})`
}
}
Если геттер зависит от других геттеров:
getters: {
userStatus(): string {
return this.isAdmin ? 'Администратор' : 'Обычный пользователь'
}
}
Использование this в геттерах возможно только при
корректной типизации store через defineStore. Pinia
автоматически связывает типы state и actions с контекстом
this.
Actions часто используются для выполнения асинхронных операций. Типизация помогает корректно определить возвращаемый тип промиса:
actions: {
async fetchUserData(userId: number): Promise<void> {
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
this.id = data.id
this.name = data.name
this.isAdmin = data.isAdmin
}
}
Особенности:
userId: number).Promise<void> или конкретный тип данных.Pinia также поддерживает подход через Composition API, когда store создаётся как набор функций и реактивных переменных:
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', () => {
const items = ref<{ id: number; name: string; quantity: number }[]>([])
const totalItems = computed(() =>
items.value.reduce((sum, item) => sum + item.quantity, 0)
)
function addItem(item: { id: number; name: string; quantity: number }) {
items.value.push(item)
}
return { items, totalItems, addItem }
})
Преимущества:
ref и computed с
полной типизацией.Для передачи типизированного store между модулями и компонентами
удобно использовать ReturnType:
type UserStoreType = ReturnType<typeof useUserStore>
const store: UserStoreType = useUserStore()
Это позволяет гарантировать корректность типов при передаче store как пропса или контекста.
state) и
использовать его при объявлении store.ref и computed.Типизация в Pinia повышает надежность кода и позволяет использовать все преимущества TypeScript в сочетании с реактивностью Vue.js, делая управление состоянием безопасным и предсказуемым.