What You’ll Build
You’ll create a type-safe user store that demonstrates:- Defining TypeScript interfaces for state
- Typing actions with proper return types
- Using the
create<State>generic
import { create } from "zustand";
interface State {
user: {
name: string;
age: number
};
isLoading: boolean;
actions: {
setUser: () => void;
clearUser: () => void;
setLoading: () => void;
};
}
const useUserStore = create<State>((set) => ({
user: {
name: "John",
age: 30
},
isLoading: false,
actions: {
setUser: () => set((state) => ({})),
clearUser: () => set((state) => ({})),
setLoading: () => set((state) => ({}))
},
}));
export default useUserStore;
import { create } from "zustand";
interface User {
name: string;
age: number;
}
interface State {
user: User | null;
isLoading: boolean;
actions: {
setUser: (user: User) => void;
clearUser: () => void;
setLoading: (loading: boolean) => void;
};
}
const useUserStore = create<State>((set) => ({
user: null,
isLoading: false,
actions: {
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
setLoading: (loading) => set({ isLoading: loading })
},
}));
export default useUserStore;
import useUserStore from '../store/useUserStore';
function UserProfile() {
const user = useUserStore((state) => state.user);
const isLoading = useUserStore((state) => state.isLoading);
const { setUser, clearUser, setLoading } = useUserStore(
(state) => state.actions
);
const handleUpdate = () => {
setLoading(true);
// TypeScript knows the exact shape of user object
setUser({ name: "Jane", age: 25 });
setLoading(false);
};
if (isLoading) return <div>Loading...</div>;
return (
<div>
{user ? (
<>
<h2>{user.name}</h2>
<p>Age: {user.age}</p>
<button onClick={clearUser}>Clear</button>
</>
) : (
<button onClick={handleUpdate}>Load User</button>
)}
</div>
);
}
export default UserProfile;
Understanding the TypeScript Template
The CLI uses this template fromtemplates/store.ts:
- Generates types from your JSON initial state
- Infers basic types:
number,boolean,string,object,any[] - Creates action type signatures as
() => void(you’ll update these)
Type Inference
The CLI’s type generation (frombin/create-zustand-store.mjs:269-296):
The CLI provides basic type inference. For complex types like arrays of specific objects or union types, you’ll need to manually refine the generated interface.
Next Steps
Persistent Store
Add persistence with TypeScript
Actions & State
Advanced action patterns