Tuesday, November 21, 2023

Zustand, devtools and immer wrapper

I’m considering a Vue 3 to React migration at work, and while writing a proof of concept, I’m standardizing the Zustand stores creation, which will all use devtools and immer middlewares. And this results in convoluted and error-prone code, which I’m trying to abstract away with a generator function. Such a function, however, has proven to be very hard to write with perfect TypeScript typings.

I had to resort to the great Daishi Kato again, which very patiently helped me to write. It indeed has a deep level of TypeScript black magic which I’m not familiar with:

import {create, StateCreator} from 'zustand';
import {devtools} from 'zustand/middleware';
import {immer} from 'zustand/middleware/immer';

export function createFullStore<T extends object, U extends object>(
	name: string,
	state: T,
	actions: StateCreator<
		T,
		[['zustand/devtools', never], ['zustand/immer', never]],
		[['zustand/immer', never], ['zustand/devtools', never]],
		U
	>,
) {
	return create<T & U>()(
		devtools(
			immer((...a) => Object.assign({}, state, (actions as any)(...a))),
			{name},
		),
	);
}

It works perfectly, with all the type inferences one would expect. I’m really thankful to the guy.

By the way this should’ve been published yesterday, but after a whole day without internet at home, I finally can do it.

No comments: