joken-前端工程师

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::
  394 随笔 :: 39 文章 :: 8 评论 :: 20万 阅读

在 Vue 3 中结合 Pinia 和 TypeScript 使用时,类型限制是一个非常重要的部分,可以帮助开发者确保状态管理的安全性和可预测性。Pinia 提供了良好的 TypeScript 支持,尤其是通过 defineStore 和相关工具类型,我们可以精确地定义 store 的状态、getters 和 actions 的类型。以下是如何在 Vue 3 中使用 Pinia 并限制 TS 类型的主要方法和示例:


1. 定义 Store 时显式声明类型

使用 defineStore 时,可以通过 TypeScript 的泛型或接口来限制 stategettersactions 的类型。

示例:基本类型限制

import { defineStore } from 'pinia';

// 定义状态的接口
interface CounterState {
  count: number;
  name: string;
}

export const useCounterStore = defineStore('counter', {
  state: (): CounterState => ({
    count: 0,
    name: 'Default',
  }),
  getters: {
    doubleCount: (state): number => state.count * 2, // 返回类型限制为 number
  },
  actions: {
    increment(payload: number) { // 参数类型限制为 number
      this.count += payload;
    },
    setName(newName: string) { // 参数类型限制为 string
      this.name = newName;
    },
  },
});
  • state:通过返回类型注解(如 : CounterState)限制状态的结构。
  • getters:通过类型注解(如 : number)限制返回值。
  • actions:通过参数类型(如 payload: number)和 this 的上下文限制操作。

使用

const counterStore = useCounterStore();
counterStore.increment(1); // OK
counterStore.increment('1'); // TS 错误:类型不匹配

2. 使用 Pinia 的内置类型

Pinia 提供了一些工具类型,可以帮助更精确地限制 store。

StoreDefinition

  • 表示 store 的完整类型,包括 stategettersactions
  • 示例:
    import { defineStore, StoreDefinition } from 'pinia';
    
    interface CounterState {
      count: number;
    }
    
    export const useCounterStore = defineStore('counter', {
      state: (): CounterState => ({ count: 0 }),
      getters: {
        double: (state): number => state.count * 2,
      },
      actions: {
        increment() {
          this.count++;
        },
      },
    });
    
    // 获取 Store 类型
    type CounterStore = ReturnType<typeof useCounterStore>;
    

PiniaCustomProperties

  • 用于扩展 store 的自定义属性。
  • 示例:
    declare module 'pinia' {
      export interface PiniaCustomProperties {
        customProp: string;
      }
    }
    
    const useStore = defineStore('store', {
      state: () => ({ count: 0 }),
    });
    
    const store = useStore();
    console.log(store.customProp); // 类型安全
    

3. 类型安全的 Getters

Getters 的类型可以通过显式注解或依赖状态类型自动推断来限制。

示例:复杂 Getters

interface UserState {
  users: { id: number; name: string }[];
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    users: [],
  }),
  getters: {
    // 类型限制为特定用户数组
    activeUsers(state): { id: number; name: string }[] {
      return state.users.filter(user => user.id > 0);
    },
    // 使用泛型限制返回单个用户
    getUserById: (state) => (id: number): { id: number; name: string } | undefined => {
      return state.users.find(user => user.id === id);
    },
  },
});
  • activeUsers:返回类型限制为用户数组。
  • getUserById:使用箭头函数返回特定类型,参数类型也受限制。

使用

const userStore = useUserStore();
const user = userStore.getUserById(1); // 类型为 { id: number; name: string } | undefined
const invalid = userStore.getUserById('1'); // TS 错误:参数类型不匹配

4. 类型安全的 Actions

Actions 可以通过参数类型和返回值类型来限制。

示例:异步 Actions

interface AuthState {
  token: string | null;
}

export const useAuthStore = defineStore('auth', {
  state: (): AuthState => ({
    token: null,
  }),
  actions: {
    async login(credentials: { username: string; password: string }): Promise<boolean> {
      // 模拟异步请求
      const response = await fakeApiCall(credentials);
      this.token = response.token;
      return !!response.token;
    },
  },
});

async function fakeApiCall(creds: { username: string; password: string }) {
  return { token: 'abc123' };
}
  • login:参数类型限制为特定对象,返回值类型限制为 Promise<boolean>

使用

const authStore = useAuthStore();
await authStore.login({ username: 'user', password: 'pass' }); // OK
await authStore.login({ username: 123, password: 'pass' }); // TS 错误:username 类型不匹配

5. 限制 Store 的订阅

Pinia 允许订阅 store 的状态变化,可以通过类型限制订阅回调。

示例

const counterStore = useCounterStore();
counterStore.$subscribe((mutation, state: CounterState) => {
  console.log(state.count); // 类型安全
  console.log(state.invalid); // TS 错误:类型不存在
});

6. 模块化 Store 类型

如果你的项目中有多个 store,可以通过类型组合来管理。

示例

// stores/counter.ts
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
});

// stores/index.ts
import { Store } from 'pinia';

interface StoreMap {
  counter: ReturnType<typeof useCounterStore>;
}

export function useStores<K extends keyof StoreMap>(key: K): StoreMap[K] {
  const storeMap: Partial<StoreMap> = {
    counter: useCounterStore(),
  };
  return storeMap[key] as StoreMap[K];
}

// 使用
const counter = useStores('counter');
counter.count; // 类型安全

7. 注意事项

  • 类型推断:Pinia 的类型系统非常强大,很多情况下可以依赖自动推断,但显式类型注解能提高代码可读性和安全性。
  • 动态属性:如果需要动态添加属性(如 this.someDynamicProp),可以通过模块增强或避免直接操作 state
  • 插件:如果使用 Pinia 插件,可能需要额外的类型声明。

综合示例

import { defineStore } from 'pinia';

interface Todo {
  id: number;
  text: string;
  done: boolean;
}

interface TodoState {
  todos: Todo[];
  filter: 'all' | 'done' | 'pending';
}

export const useTodoStore = defineStore('todo', {
  state: (): TodoState => ({
    todos: [],
    filter: 'all',
  }),
  getters: {
    filteredTodos(state): Todo[] {
      if (state.filter === 'done') return state.todos.filter(t => t.done);
      if (state.filter === 'pending') return state.todos.filter(t => !t.done);
      return state.todos;
    },
  },
  actions: {
    addTodo(text: string) {
      this.todos.push({ id: Date.now(), text, done: false });
    },
    toggleTodo(id: number) {
      const todo = this.todos.find(t => t.id === id);
      if (todo) todo.done = !todo.done;
    },
    setFilter(filter: TodoState['filter']) {
      this.filter = filter;
    },
  },
});

// 使用
const todoStore = useTodoStore();
todoStore.addTodo('Learn Pinia'); // OK
todoStore.addTodo(123); // TS 错误
todoStore.setFilter('done'); // OK
todoStore.setFilter('invalid'); // TS 错误

总结

在 Vue 3 中使用 Pinia 时,TS 类型的限制主要依靠:

  1. 接口/类型定义:为 stategettersactions 定义明确结构。
  2. 内置工具类型:如 StoreDefinitionPiniaCustomProperties
  3. 类型注解:显式声明参数和返回值类型。
  4. 类型推断:利用 Pinia 的 TS 支持减少冗余代码。
posted on   joken1310  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示