Pinia TypeScript 集成 #
概述 #
Pinia 从设计之初就考虑了 TypeScript 支持,提供了开箱即用的类型推断。本节将介绍如何在 Pinia 中充分利用 TypeScript。
基本类型定义 #
State 类型 #
ts
// stores/user.ts
import { defineStore } from 'pinia'
interface User {
id: number
name: string
email: string
role: 'admin' | 'user'
}
interface UserState {
user: User | null
token: string | null
loading: boolean
error: string | null
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
user: null,
token: null,
loading: false,
error: null
})
})
Getters 类型 #
ts
interface UserGetters {
isAuthenticated: boolean
displayName: string
isAdmin: boolean
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({ /* ... */ }),
getters: {
// 自动推断类型
isAuthenticated: (state): boolean => !!state.token,
// 显式指定返回类型
displayName(): string {
return this.user?.name || 'Guest'
},
// 复杂类型
isAdmin(): boolean {
return this.user?.role === 'admin'
}
}
})
Actions 类型 #
ts
interface UserActions {
login: (email: string, password: string) => Promise<boolean>
logout: () => void
updateProfile: (data: Partial<User>) => void
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({ /* ... */ }),
actions: {
async login(email: string, password: string): Promise<boolean> {
try {
const response = await api.login({ email, password })
this.user = response.user
this.token = response.token
return true
} catch {
return false
}
},
logout(): void {
this.user = null
this.token = null
},
updateProfile(data: Partial<User>): void {
if (this.user) {
Object.assign(this.user, data)
}
}
}
})
完整类型定义 #
使用泛型 #
ts
// stores/types.ts
export interface StoreState<T> {
data: T | null
loading: boolean
error: string | null
}
export interface StoreGetters<T> {
hasData: boolean
isLoading: boolean
hasError: boolean
}
export interface StoreActions<T> {
fetch: () => Promise<void>
reset: () => void
}
ts
// stores/product.ts
import { defineStore } from 'pinia'
interface Product {
id: number
name: string
price: number
}
interface ProductState extends StoreState<Product[]> {
selectedProduct: Product | null
}
export const useProductStore = defineStore('product', {
state: (): ProductState => ({
data: null,
loading: false,
error: null,
selectedProduct: null
}),
getters: {
hasData: (state): boolean => state.data !== null && state.data.length > 0,
isLoading: (state): boolean => state.loading,
hasError: (state): boolean => state.error !== null
},
actions: {
async fetch(): Promise<void> {
this.loading = true
try {
const response = await fetch('/api/products')
this.data = await response.json()
} catch (e) {
this.error = e instanceof Error ? e.message : 'Unknown error'
} finally {
this.loading = false
}
},
reset(): void {
this.data = null
this.loading = false
this.error = null
this.selectedProduct = null
}
}
})
Setup Store 类型 #
ref 类型 #
ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
// ref 自动推断类型
const count = ref(0)
// 显式类型
const name = ref<string>('')
// 复杂类型
const user = ref<User | null>(null)
// computed 自动推断
const double = computed(() => count.value * 2)
// 显式 computed 类型
const isAdmin = computed<boolean>(() => user.value?.role === 'admin')
return { count, name, user, double, isAdmin }
})
函数类型 #
ts
export const useUserStore = defineStore('user', () => {
const user = ref<User | null>(null)
// 函数参数和返回值类型
async function login(email: string, password: string): Promise<boolean> {
try {
const response = await api.login({ email, password })
user.value = response.user
return true
} catch {
return false
}
}
function updateProfile(data: Partial<User>): void {
if (user.value) {
Object.assign(user.value, data)
}
}
return { user, login, updateProfile }
})
扩展 Pinia 类型 #
自定义 Store 属性 #
ts
// types/pinia.d.ts
import 'pinia'
import type { Router } from 'vue-router'
declare module 'pinia' {
interface PiniaCustomProperties {
// 添加 $router 属性
$router: Router
// 添加 $api 方法
$api: {
get: <T>(url: string) => Promise<T>
post: <T>(url: string, data: any) => Promise<T>
}
}
}
ts
// main.ts
import { createPinia } from 'pinia'
import { router } from './router'
const pinia = createPinia()
pinia.use(({ store }) => {
store.$router = router
store.$api = {
async get<T>(url: string): Promise<T> {
const response = await fetch(url)
return response.json()
},
async post<T>(url: string, data: any): Promise<T> {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
return response.json()
}
}
})
自定义 State 属性 #
ts
// types/pinia.d.ts
declare module 'pinia' {
interface PiniaCustomStateProperties {
// 添加版本号
_version: number
}
}
类型工具 #
提取 Store 类型 #
ts
// utils/store.ts
import type { Store, StoreState, StoreGetters, StoreActions } from 'pinia'
// 提取 State 类型
export type ExtractState<S> = S extends Store<string, infer State, any, any>
? State
: never
// 提取 Getters 类型
export type ExtractGetters<S> = S extends Store<string, any, infer Getters, any>
? Getters
: never
// 提取 Actions 类型
export type ExtractActions<S> = S extends Store<string, any, any, infer Actions>
? Actions
: never
ts
// 使用
import { useUserStore } from '@/stores/user'
type UserState = ExtractState<ReturnType<typeof useUserStore>>
type UserGetters = ExtractGetters<ReturnType<typeof useUserStore>>
type UserActions = ExtractActions<ReturnType<typeof useUserStore>>
创建类型安全的 Store #
ts
// utils/defineTypedStore.ts
import { defineStore } from 'pinia'
interface StoreDefinition<
Id extends string,
S extends Record<string, any>,
G extends Record<string, any>,
A extends Record<string, any>
> {
state: () => S
getters: G & ThisType<S & G>
actions: A & ThisType<S & G & A>
}
export function defineTypedStore<
Id extends string,
S extends Record<string, any>,
G extends Record<string, any>,
A extends Record<string, any>
>(
id: Id,
definition: StoreDefinition<Id, S, G, A>
) {
return defineStore(id, definition)
}
实际示例 #
完整类型化的用户 Store #
ts
// stores/user.ts
import { defineStore } from 'pinia'
// 类型定义
interface User {
id: number
name: string
email: string
role: 'admin' | 'user'
avatar?: string
}
interface UserState {
user: User | null
token: string | null
loading: boolean
error: string | null
}
interface UserGetters {
isAuthenticated: boolean
displayName: string
isAdmin: boolean
avatarUrl: string
}
interface UserActions {
login: (email: string, password: string) => Promise<boolean>
logout: () => void
fetchUser: () => Promise<void>
updateProfile: (data: Partial<User>) => Promise<boolean>
}
// Store 定义
export const useUserStore = defineStore('user', {
state: (): UserState => ({
user: null,
token: localStorage.getItem('token'),
loading: false,
error: null
}),
getters: {
isAuthenticated(): boolean {
return !!this.token && !!this.user
},
displayName(): string {
return this.user?.name || 'Guest'
},
isAdmin(): boolean {
return this.user?.role === 'admin'
},
avatarUrl(): string {
return this.user?.avatar || '/default-avatar.png'
}
},
actions: {
async login(email: string, password: string): Promise<boolean> {
this.loading = true
this.error = null
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
})
if (!response.ok) {
throw new Error('Login failed')
}
const data = await response.json()
this.user = data.user
this.token = data.token
localStorage.setItem('token', data.token)
return true
} catch (e) {
this.error = e instanceof Error ? e.message : 'Login failed'
return false
} finally {
this.loading = false
}
},
logout(): void {
this.user = null
this.token = null
localStorage.removeItem('token')
},
async fetchUser(): Promise<void> {
if (!this.token) return
this.loading = true
try {
const response = await fetch('/api/user', {
headers: { Authorization: `Bearer ${this.token}` }
})
this.user = await response.json()
} catch {
this.logout()
} finally {
this.loading = false
}
},
async updateProfile(data: Partial<User>): Promise<boolean> {
if (!this.user) return false
this.loading = true
try {
const response = await fetch(`/api/user/${this.user.id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.token}`
},
body: JSON.stringify(data)
})
this.user = await response.json()
return true
} catch {
return false
} finally {
this.loading = false
}
}
}
})
// 导出类型供其他模块使用
export type { User, UserState, UserGetters, UserActions }
最佳实践 #
1. 始终定义 State 类型 #
ts
// 推荐
interface UserState {
user: User | null
loading: boolean
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
user: null,
loading: false
})
})
// 不推荐
export const useUserStore = defineStore('user', {
state: () => ({
user: null, // 类型不明确
loading: false
})
})
2. 使用严格类型 #
ts
// 推荐:使用联合类型
role: 'admin' | 'user'
// 不推荐:使用 string
role: string
3. 导出类型 #
ts
// 导出类型供其他模块使用
export type { User, UserState }
4. 使用类型守卫 #
ts
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'id' in value
}
// 使用
if (isUser(data)) {
this.user = data
}
下一步 #
现在你已经掌握了 TypeScript 集成,接下来让我们学习性能优化。
- 性能优化 - 性能优化技巧
最后更新:2026-03-28