Skip to content

Commit

Permalink
feat(middleware): implement mutative middleware for zustand
Browse files Browse the repository at this point in the history
  • Loading branch information
unadlib committed May 18, 2024
1 parent e42cd99 commit fcb511b
Showing 1 changed file with 70 additions and 1 deletion.
71 changes: 70 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,70 @@
export const add = (a: number, b: number) => a + b;
import { create } from 'mutative';
import type { Draft } from 'mutative';
import type { StateCreator, StoreMutatorIdentifier } from 'zustand';

type Mutative = <
T,
Mps extends [StoreMutatorIdentifier, unknown][] = [],
Mcs extends [StoreMutatorIdentifier, unknown][] = []
>(
initializer: StateCreator<T, [...Mps, ['zustand/mutative', never]], Mcs>
) => StateCreator<T, Mps, [['zustand/mutative', never], ...Mcs]>;

type Write<T, U> = Omit<T, keyof U> & U;
type SkipTwo<T> = T extends { length: 0 }
? []
: T extends { length: 1 }
? []
: T extends { length: 0 | 1 }
? []
: T extends [unknown, unknown, ...infer A]
? A
: T extends [unknown, unknown?, ...infer A]
? A
: T extends [unknown?, unknown?, ...infer A]
? A
: never;

declare module 'zustand/vanilla' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface StoreMutators<S, A> {
['zustand/mutative']: WithMutative<S>;
}
}

type WithMutative<S> = Write<S, StoreMutative<S>>;

type StoreMutative<S> = S extends {
getState: () => infer T;
setState: infer SetState;
}
? SetState extends (...a: infer A) => infer Sr
? {
setState(
nextStateOrUpdater: T | Partial<T> | ((state: Draft<T>) => void),
shouldReplace?: boolean | undefined,
...a: SkipTwo<A>
): Sr;
}
: never
: never;

type MutativeImpl = <T>(
storeInitializer: StateCreator<T, [], []>
) => StateCreator<T, [], []>;

const mutativeImpl: MutativeImpl = (initializer) => (set, get, store) => {
type T = ReturnType<typeof initializer>;

store.setState = (updater, replace, ...a) => {
const nextState = (
typeof updater === 'function' ? create(updater as any) : updater
) as ((s: T) => T) | T | Partial<T>;

return set(nextState as any, replace, ...a);
};

return initializer(store.setState, get, store);
};

export const mutative = mutativeImpl as unknown as Mutative;

0 comments on commit fcb511b

Please sign in to comment.