Files
gh-jezweb-claude-skills-ski…/references/typescript-patterns.md
2025-11-30 08:25:53 +08:00

4.9 KiB

Zustand TypeScript Advanced Patterns

Advanced TypeScript patterns and troubleshooting. Load when encountering complex type inference issues.


Basic TypeScript Setup

The Golden Rule: Double Parentheses

// ✅ CORRECT
const useStore = create<Store>()((set) => ({ /* ... */ }))

// ❌ WRONG
const useStore = create<Store>((set) => ({ /* ... */ }))

Why: Currying syntax enables middleware type inference.


Store Interface Pattern

// Define types
interface BearState {
  bears: number
}

interface BearActions {
  increase: (by: number) => void
  decrease: (by: number) => void
}

// Combine
type BearStore = BearState & BearActions

// Use
const useBearStore = create<BearStore>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
  decrease: (by) => set((state) => ({ bears: state.bears - by })),
}))

Slices Pattern Types

import { StateCreator } from 'zustand'

// Define slice type
interface BearSlice {
  bears: number
  addBear: () => void
}

// Create slice with proper types
const createBearSlice: StateCreator<
  BearSlice & FishSlice,  // Combined store type
  [],                      // Middleware mutators
  [],                      // Chained middleware
  BearSlice               // This slice
> = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
})

Middleware Types

With Devtools

import { StateCreator } from 'zustand'
import { devtools } from 'zustand/middleware'

interface Store {
  count: number
  increment: () => void
}

const useStore = create<Store>()(
  devtools(
    (set) => ({
      count: 0,
      increment: () =>
        set(
          (state) => ({ count: state.count + 1 }),
          undefined,
          'increment'
        ),
    }),
    { name: 'Store' }
  )
)

With Persist

import { persist } from 'zustand/middleware'

const useStore = create<Store>()(
  persist(
    (set) => ({ /* ... */ }),
    { name: 'storage' }
  )
)

With Multiple Middlewares

const useStore = create<Store>()(
  devtools(
    persist(
      (set) => ({ /* ... */ }),
      { name: 'storage' }
    ),
    { name: 'Store' }
  )
)

Slices with Middleware

import { StateCreator } from 'zustand'
import { devtools } from 'zustand/middleware'

const createBearSlice: StateCreator<
  BearSlice & FishSlice,
  [['zustand/devtools', never]],  // Add devtools mutator
  [],
  BearSlice
> = (set) => ({
  bears: 0,
  addBear: () =>
    set(
      (state) => ({ bears: state.bears + 1 }),
      undefined,
      'bear/add'
    ),
})

Selector Types

Basic Selector

const bears = useStore((state) => state.bears)
// Type: number

Computed Selector

const selectTotal = (state: Store) => state.items.reduce((sum, item) => sum + item.price, 0)

const total = useStore(selectTotal)
// Type: number

Parameterized Selector

const selectById = (id: string) => (state: Store) =>
  state.items.find((item) => item.id === id)

const item = useStore(selectById('123'))
// Type: Item | undefined

Vanilla Store Types

import { createStore } from 'zustand/vanilla'

const store = createStore<Store>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// Get state
const state = store.getState()
// Type: Store

// Subscribe
const unsubscribe = store.subscribe((state) => {
  // state is typed as Store
})

Custom Hook with Types

import { useStore } from 'zustand'

const bearStore = createStore<BearStore>()((set) => ({
  bears: 0,
  increase: () => set((state) => ({ bears: state.bears + 1 })),
}))

// Create custom hook
function useBearStore<T>(selector: (state: BearStore) => T): T {
  return useStore(bearStore, selector)
}

Common Type Errors

Error: Type inference breaks

Problem: Using single parentheses

// ❌ WRONG
const useStore = create<Store>((set) => ({ /* ... */ }))

Solution: Use double parentheses

// ✅ CORRECT
const useStore = create<Store>()((set) => ({ /* ... */ }))

Error: StateCreator types fail

Problem: Missing middleware mutators

// ❌ WRONG
const createSlice: StateCreator<CombinedStore, [], [], MySlice>

Solution: Add middleware mutators

// ✅ CORRECT
const createSlice: StateCreator<
  CombinedStore,
  [['zustand/devtools', never]],  // Add this
  [],
  MySlice
>

Error: Circular reference

Problem: Slices referencing each other

Solution: Define combined type first, then reference in slices

type AllSlices = BearSlice & FishSlice & SharedSlice

const createBearSlice: StateCreator<AllSlices, [], [], BearSlice> = ...

Official TypeScript Guide

https://zustand.docs.pmnd.rs/guides/typescript