Skip to content

Commit 3871d53

Browse files
wheerddai-shi
andauthored
fix(immer): Proper typing for immer middleware in combination with slices (#3371)
* Add generic type parameter U to Immer type * Add a test for immer + sliced store typing * Rename test case for clarity on slice type --------- Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>
1 parent 9b505ac commit 3871d53

2 files changed

Lines changed: 53 additions & 2 deletions

File tree

src/middleware/immer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ type Immer = <
66
T,
77
Mps extends [StoreMutatorIdentifier, unknown][] = [],
88
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
9+
U = T,
910
>(
10-
initializer: StateCreator<T, [...Mps, ['zustand/immer', never]], Mcs>,
11-
) => StateCreator<T, Mps, [['zustand/immer', never], ...Mcs]>
11+
initializer: StateCreator<T, [...Mps, ['zustand/immer', never]], Mcs, U>,
12+
) => StateCreator<T, Mps, [['zustand/immer', never], ...Mcs], U>
1213

1314
declare module '../vanilla' {
1415
// eslint-disable-next-line @typescript-eslint/no-unused-vars

tests/middlewareTypes.test.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,3 +785,53 @@ describe('create with explicitly annotated mutators', () => {
785785
expect(TestComponent).toBeDefined()
786786
})
787787
})
788+
789+
describe('single middleware with sliced store', () => {
790+
it('immer with slices where slice type differs from store (#3371)', () => {
791+
interface BearSlice {
792+
bears: number
793+
addBear: () => void
794+
eatFish: () => void
795+
}
796+
797+
interface FishSlice {
798+
fishes: number
799+
addFish: () => void
800+
}
801+
802+
const createBearSlice: StateCreator<
803+
BearSlice & FishSlice,
804+
[['zustand/immer', never]],
805+
[],
806+
BearSlice
807+
> = (set) => ({
808+
bears: 0,
809+
addBear: () => set((state) => ({ bears: state.bears + 1 })),
810+
eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
811+
})
812+
813+
const createFishSlice: StateCreator<
814+
BearSlice & FishSlice,
815+
[['zustand/immer', never]],
816+
[],
817+
FishSlice
818+
> = (set) => ({
819+
fishes: 0,
820+
addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
821+
})
822+
823+
const useBoundStore = create<BearSlice & FishSlice>()((...a) => ({
824+
...immer(createBearSlice)(...a),
825+
...immer(createFishSlice)(...a),
826+
}))
827+
828+
const TestComponent = () => {
829+
expectTypeOf(useBoundStore((s) => s.bears)).toEqualTypeOf<number>()
830+
expectTypeOf(useBoundStore((s) => s.fishes)).toEqualTypeOf<number>()
831+
expectTypeOf(useBoundStore((s) => s.addBear)()).toEqualTypeOf<void>()
832+
expectTypeOf(useBoundStore((s) => s.eatFish)()).toEqualTypeOf<void>()
833+
return <></>
834+
}
835+
expect(TestComponent).toBeDefined()
836+
})
837+
})

0 commit comments

Comments
 (0)