You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置Zustand只读存储?TypeScript类型适配问题咨询

确保Zustand存储类型为只读的解决方案

1. 给根状态标记深度只读,从源头限制

直接在定义Zustand根状态类型时使用ReadonlyDeep,让整个状态树默认是深度只读的,避免在lens层面单独处理导致类型冲突:

import { ReadonlyDeep } from 'type-fest';
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

// 根状态直接标记为深度只读
type AppState = ReadonlyDeep<{
  todos: { id: string; text: string }[];
  filters: { status: 'all' | 'active' | 'completed' };
}>;

const useStore = create<AppState>()(
  immer((set) => ({
    todos: [],
    filters: { status: 'all' },
    addTodo: (text) =>
      set((draft) => {
        // Immer的draft是可变的,这里可以正常调用push等方法
        draft.todos.push({ id: Date.now().toString(), text });
      }),
    sortTodos: () =>
      set((draft) => {
        // 即使在draft里用sort也没问题,但如果直接操作外部取出的state会报错
        draft.todos.sort((a, b) => a.text.localeCompare(b.text));
      }),
  }))
);

这样定义后,直接从store取出的状态是只读的,比如const state = useStore(),此时调用state.todos.sort()会被TypeScript直接拦截,提前避免变异操作。

2. 让Lens自动继承根状态的只读性

不需要手动给Lens切片添加ReadonlyDeep,Zustand的Lens会自动从根状态推导类型,根状态是深度只读的话,切片自然也是只读的:

import { createLens } from 'zustand/lens';

// 自动推导得到只读的todos切片类型
const useTodosLens = createLens(useStore, (state) => state.todos);

// 尝试调用变异方法会直接触发TS报错
const todos = useTodosLens();
todos.sort(); // ❌ TypeScript错误:sort方法会修改原数组,而todos是只读数组

3. 解决手动给Lens加ReadonlyDeep导致的TS问题

之前遇到的类型冲突,本质是手动给Lens指定ReadonlyDeep后,和Zustand Lens内部的类型推导逻辑冲突。正确的做法是完全依赖根状态的只读标记,让Lens自动继承类型,不要手动给切片加额外的只读修饰。

关键注意点

  • Immer的set回调参数是可变的draft对象,这里可以正常使用push、sort等变异方法,Immer会自动将其转换为不可变的状态更新。
  • 从store直接取出的状态(非draft)是只读的,TypeScript会禁止任何变异操作,从语法层面提前拦截类似sort的错误调用。

内容的提问来源于stack exchange,提问作者Francesco Meli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 23:12:16