According to zustand documentation, it's impossible to infer the store type implicitly.
zustand-types
utilizes an unconventional technique to correctly infer the store type both within and outside the store function. This method works perfectly under freshly released TypeScript 5, even in strict mode.
npm install -D zustand-types
yarn add -D zustand-types
Extract a store as a named function and provide SetState<typeof MyStore>
type. That's it!
import {create} from "zustand"
import {SetState} from "zustand-types"
function MyStore(set: SetState<typeof MyStore>) {
return {
pets: [] as string[],
// state: correctly inferred
addPet: (pet: string) => set((state) => [...state.pets, pet]),
}
}
// useStore: correctly inferred
export const useStore = create(MyStore)
In case you need two other parameters, get
and store
, their types are also provided.
import {create} from "zustand"
import {SetState, GetState, Store} from "zustand-types"
function MyStore(
set: SetState<typeof MyStore>,
get: GetState<typeof MyStore>,
store: Store<typeof MyStore>,
) {
const state = get()
const unsubscribe = store.subscribe((state) => {})
return {
//...
}
}
export const useStore = create(MyStore)
Slice pattern is a way to split your big store into multiple stores.
zustand-types
provides a single type StoreArgs
that captures all three arguments .
import {create} from "zustand"
import {StoreArgs} from "../src"
// Option A
function Store1(...args: StoreArgs<typeof Store1>) {
const [set] = args
return {
x: 1,
setX: (x: number) => set({x}),
}
}
// Option B
function Store2(set: SetState<typeof Store2>, ...args: any[]) {
return {
y: 1,
setY: (y: number) => set({y}),
}
}
function Combined(...args: StoreArgs<typeof Combined>) {
const state1 = Store1(...args)
const state2 = Store2(...args)
// Each slice state must be assigned to a variable, otherwise `Combined` return type will become implicit `any`.
return {...state1, ...state2}
}
export const useStore = create(Combined)