# TypeScript 类型定义指南

URL: https://caijiao.org/zustand/guide/typescript
Source: docs/zustand/guide/typescript.md
Description: 学习如何在 Zustand 中使用 TypeScript 进行严格的类型定义，包括自动类型推断、显式类型定义、类型化 Actions、异步 Actions 类型、中间件类型、选择器类型以及高级类型模式等内容。

[Zustand](../) 提供了出色的 [TypeScript](/typescript/) 支持，允许你为 Store 定义严格的类型，从而获得更好的开发体验和类型安全。本指南将详细介绍如何在 Zustand 中使用 TypeScript 进行类型定义。

## 基本类型定义

### 自动类型推断

Zustand 可以自动推断 Store 的类型，无需显式定义：

```typescript
import { create } from 'zustand'

const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}))

// TypeScript 会自动推断以下类型：
// useCounterStore: () => {
//   count: number;
//   increment: () => void;
//   decrement: () => void;
//   reset: () => void;
// }
```

### 显式类型定义

对于更复杂的 Store，你可以显式定义 Store 的类型：

```typescript
import { create } from 'zustand'

// 定义 Store 类型
interface CounterStore {
  count: number;
  increment: () => void;
  decrement: () => void;
  reset: () => void;
  setCount: (count: number) => void;
}

// 创建 Store 并应用类型
const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
  setCount: (count) => set({ count }),
}))
```

## 类型化 Actions

### 基本 Actions 类型

```typescript
import { create } from 'zustand'

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

interface TodoStore {
  todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: number) => void;
  deleteTodo: (id: number) => void;
  updateTodo: (id: number, text: string) => void;
}

const useTodoStore = create<TodoStore>((set) => ({
  todos: [],
  addTodo: (text) => set((state) => ({
    todos: [...state.todos, { id: Date.now(), text, completed: false }],
  })),
  toggleTodo: (id) => set((state) => ({
    todos: state.todos.map((todo) =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ),
  })),
  deleteTodo: (id) => set((state) => ({
    todos: state.todos.filter((todo) => todo.id !== id),
  })),
  updateTodo: (id, text) => set((state) => ({
    todos: state.todos.map((todo) =>
      todo.id === id ? { ...todo, text } : todo
    ),
  })),
}))
```

### 使用 `SetState` 类型

你可以使用 `SetState` 类型来定义 `set` 函数的类型：

```typescript
import { create, SetState } from 'zustand'

interface CounterStore {
  count: number;
  increment: () => void;
  multiply: (factor: number) => void;
}

const createCounterStore = (set: SetState<CounterStore>) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  multiply: (factor) => set((state) => ({ count: state.count * factor })),
})

const useCounterStore = create<CounterStore>(createCounterStore)
```

## 异步 Actions 类型

```typescript
import { create } from 'zustand'

interface User {
  id: number;
  name: string;
  email: string;
}

interface UserStore {
  users: User[];
  isLoading: boolean;
  error: string | null;
  fetchUsers: () => Promise<void>;
  fetchUser: (id: number) => Promise<User | null>;
  addUser: (user: Omit<User, 'id'>) => Promise<void>;
}

const useUserStore = create<UserStore>((set, get) => ({
  users: [],
  isLoading: false,
  error: null,
  fetchUsers: async () => {
    set({ isLoading: true, error: null })
    try {
      const response = await fetch('/api/users')
      const users = await response.json()
      set({ users, isLoading: false })
    } catch (error) {
      set({ error: error instanceof Error ? error.message : '未知错误', isLoading: false })
    }
  },
  fetchUser: async (id) => {
    set({ isLoading: true, error: null })
    try {
      const response = await fetch(`/api/users/${id}`)
      if (!response.ok) throw new Error('用户不存在')
      const user = await response.json()
      set({ isLoading: false })
      return user
    } catch (error) {
      set({ error: error instanceof Error ? error.message : '未知错误', isLoading: false })
      return null
    }
  },
  addUser: async (userData) => {
    set({ isLoading: true, error: null })
    try {
      const response = await fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(userData),
      })
      const newUser = await response.json()
      set((state) => ({ users: [...state.users, newUser], isLoading: false }))
    } catch (error) {
      set({ error: error instanceof Error ? error.message : '未知错误', isLoading: false })
    }
  },
}))
```

## 中间件类型

### Persist 中间件类型

```typescript
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface CounterStore {
  count: number;
  increment: () => void;
}

const useCounterStore = create<CounterStore>()(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'counter-storage',
      storage: createJSONStorage(() => localStorage),
    }
  )
)
```

### Immer 中间件类型

```typescript
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

interface TodoStore {
  todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: number) => void;
}

const useTodoStore = create<TodoStore>()(
  immer((set) => ({
    todos: [],
    addTodo: (text) =>
      set((state) => {
        state.todos.push({ id: Date.now(), text, completed: false })
      }),
    toggleTodo: (id) =>
      set((state) => {
        const todo = state.todos.find((todo) => todo.id === id)
        if (todo) todo.completed = !todo.completed
      }),
  }))
)
```

### 组合中间件类型

```typescript
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'

interface CounterStore {
  count: number;
  increment: () => void;
  multiply: (factor: number) => void;
}

const useCounterStore = create<CounterStore>()(
  devtools(
    persist(
      immer((set) => ({
        count: 0,
        increment: () => set((state) => { state.count += 1 }),
        multiply: (factor) => set((state) => { state.count *= factor }),
      })),
      {
        name: 'counter-storage',
      }
    )
  )
)
```

## Selector 类型

### 基本 Selector 类型

```typescript
import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

interface UserStore {
  users: User[];
  selectedUserId: number | null;
  selectUser: (id: number) => void;
}

const useUserStore = create<UserStore>((set) => ({
  users: [],
  selectedUserId: null,
  selectUser: (id) => set({ selectedUserId: id }),
}))

// 使用 Selector
function UserProfile() {
  // 基本 Selector
  const selectedUserId = useUserStore((state) => state.selectedUserId)
  
  // 组合 Selector
  const { name, email } = useUserStore(
    (state) => {
      const user = state.users.find((u) => u.id === state.selectedUserId)
      return { name: user?.name || '', email: user?.email || '' }
    },
    useShallow() // 浅比较
  )
  
  // ...
}
```

### 自定义 Selector 类型

```typescript
import { create } from 'zustand'

interface CounterStore {
  count: number;
  increment: () => void;
}

const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// 自定义 Selector 类型
const useCount = () => useCounterStore((state) => state.count)
const useIncrement = () => useCounterStore((state) => state.increment)

// 在组件中使用
function Counter() {
  const count = useCount()
  const increment = useIncrement()
  
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
    </div>
  )
}
```

## 高级类型模式

### 使用 `createStore` 类型

```typescript
import { create, createStore } from 'zustand'

interface CounterStore {
  count: number;
  increment: () => void;
}

// 创建 Store 实例
const counterStore = createStore<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// 创建 Hook
const useCounterStore = create<CounterStore>(() => counterStore)

// 直接使用 Store 实例（非 React 环境）
counterStore.getState().increment()
```

### 扩展 Store 类型

```typescript
import { create } from 'zustand'

interface BaseCounterStore {
  count: number;
  increment: () => void;
}

interface ExtendedCounterStore extends BaseCounterStore {
  multiply: (factor: number) => void;
  reset: () => void;
}

const useBaseCounterStore = create<BaseCounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

const useExtendedCounterStore = create<ExtendedCounterStore>((set, get) => ({
  ...useBaseCounterStore.getState(),
  multiply: (factor) => set((state) => ({ count: state.count * factor })),
  reset: () => set({ count: 0 }),
}))
```

### 依赖注入类型

```typescript
import { create } from 'zustand'

interface Config {
  apiUrl: string;
}

interface UserStore {
  users: User[];
  fetchUsers: () => Promise<void>;
}

interface User {
  id: number;
  name: string;
}

const createUserStore = (config: Config) => {
  return create<UserStore>((set) => ({
    users: [],
    fetchUsers: async () => {
      const response = await fetch(`${config.apiUrl}/users`)
      const users = await response.json()
      set({ users })
    },
  }))
}

// 使用
const useUserStore = createUserStore({ apiUrl: 'https://api.example.com' })
```

## 类型安全的 State 更新

### 使用 `Partial` 更新部分状态

```typescript
import { create } from 'zustand'

interface UserStore {
  user: {
    id: number;
    name: string;
    email: string;
    age: number;
  };
  updateUser: (updates: Partial<UserStore['user']>) => void;
}

const useUserStore = create<UserStore>((set) => ({
  user: {
    id: 1,
    name: '张三',
    email: 'zhangsan@example.com',
    age: 30,
  },
  updateUser: (updates) =>
    set((state) => ({
      user: { ...state.user, ...updates },
    })),
}))

// 使用
useUserStore.getState().updateUser({ name: '李四' }) // 类型安全
```

### 使用 `Omit` 和 `Pick` 类型

```typescript
import { create } from 'zustand'

interface Todo {
  id: number;
  text: string;
  completed: boolean;
  createdAt: Date;
  updatedAt: Date;
}

interface TodoStore {
  todos: Todo[];
  addTodo: (todo: Omit<Todo, 'id' | 'createdAt' | 'updatedAt'>) => void;
  updateTodo: (id: number, updates: Pick<Todo, 'text' | 'completed'>) => void;
}

const useTodoStore = create<TodoStore>((set) => ({
  todos: [],
  addTodo: (todo) =>
    set((state) => ({
      todos: [
        ...state.todos,
        {
          ...todo,
          id: Date.now(),
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      ],
    })),
  updateTodo: (id, updates) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id
          ? { ...todo, ...updates, updatedAt: new Date() }
          : todo
      ),
    })),
}))
```

## 最佳实践

### 1. 显式定义 Store 类型

虽然 Zustand 支持自动类型推断，但显式定义 Store 类型可以使代码更清晰，尤其是对于复杂的 Store：

```typescript
// 推荐：显式定义类型
interface CounterStore {
  count: number;
  increment: () => void;
}

const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))
```

### 2. 使用 `readonly` 保护不可变状态

```typescript
import { create } from 'zustand'

interface Todo {
  readonly id: number;
  text: string;
  completed: boolean;
  readonly createdAt: Date;
}

interface TodoStore {
  readonly todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: number) => void;
}
```

### 3. 分离类型定义和实现

将类型定义放在单独的文件中，可以提高代码的可维护性：

```typescript
// store/types.ts
export interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

export interface TodoStore {
  todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: number) => void;
}

// store/index.ts
import { create } from 'zustand'
import { TodoStore } from './types'

export const useTodoStore = create<TodoStore>((set) => ({
  // 实现
}))
```

### 4. 使用 `unknown` 处理外部数据

```typescript
import { create } from 'zustand'

interface User {
  id: number;
  name: string;
  email: string;
}

interface UserStore {
  users: User[];
  fetchUsers: () => Promise<void>;
}

const useUserStore = create<UserStore>((set) => ({
  users: [],
  fetchUsers: async () => {
    const response = await fetch('/api/users')
    const data: unknown = await response.json()
    
    // 验证数据类型
    if (Array.isArray(data) && data.every(item => 
      typeof item === 'object' && item !== null && 'id' in item
    )) {
      set({ users: data as User[] })
    }
  },
}))
```

### 5. 为中间件配置添加类型

```typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface CounterStore {
  count: number;
  increment: () => void;
}

const useCounterStore = create<CounterStore>()(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'counter-storage',
      // 为 storage 配置添加类型
      storage: {
        getItem: (name) => {
          const item = localStorage.getItem(name)
          return item ? JSON.parse(item) : null
        },
        setItem: (name, value) => {
          localStorage.setItem(name, JSON.stringify(value))
        },
        removeItem: (name) => {
          localStorage.removeItem(name)
        },
      },
    }
  )
)
```

## 常见问题与解决方案

### 1. 类型错误：`set` 函数无法推断状态类型

**问题**：

```typescript
const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  // 类型错误：state.count 可能为 undefined
}))
```

**解决方案**：显式定义 Store 类型

```typescript
interface CounterStore {
  count: number;
  increment: () => void;
}

const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))
```

### 2. 类型错误：中间件组合时类型不匹配

**问题**：

```typescript
const useCounterStore = create(
  devtools(
    persist(
      (set) => ({
        count: 0,
        increment: () => set((state) => ({ count: state.count + 1 })),
      })
    )
  )
)
```

**解决方案**：使用泛型工厂函数语法

```typescript
const useCounterStore = create<CounterStore>()(
  devtools(
    persist(
      (set) => ({
        count: 0,
        increment: () => set((state) => ({ count: state.count + 1 })),
      })
    )
  )
)
```

### 3. 类型错误：Selector 返回类型不明确

**问题**：

```typescript
const userData = useUserStore((state) => {
  const user = state.users.find(u => u.id === state.selectedUserId)
  return { name: user?.name, email: user?.email }
})
```

**解决方案**：显式定义 Selector 返回类型

```typescript
const userData = useUserStore<{ name: string | undefined; email: string | undefined }>(
  (state) => {
    const user = state.users.find(u => u.id === state.selectedUserId)
    return { name: user?.name, email: user?.email }
  }
)
```

## 总结

Zustand 的 TypeScript 支持提供了强大的类型安全功能，使你能够：

1. **定义严格的 Store 类型**：确保状态和操作的类型一致性
2. **自动类型推断**：减少样板代码，提高开发效率
3. **中间件类型支持**：为 persist、immer 等中间件提供类型定义
4. **类型安全的状态更新**：确保状态更新符合预期的类型
5. **高级类型模式**：支持扩展 Store、依赖注入等高级模式

通过遵循本指南中的最佳实践，你可以充分利用 TypeScript 的优势，构建更可靠、更易于维护的 Zustand 应用。

在接下来的章节中，我们将学习 [非 React 组件中使用 Store](vanilla-js-usage.md)，了解如何在非 React 环境中使用 Zustand Store。
