Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking(types): Add higher kinded mutator types #725

Merged
merged 250 commits into from
Apr 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
250 commits
Select commit Hold shift + click to select a range
cf511cf
imaginary code that uses uSES
dai-shi Aug 31, 2021
5570b10
revert backward compatibility code as this is not going to be v4
dai-shi Aug 31, 2021
b03b226
use use-sync-external-store
dai-shi Sep 4, 2021
8112285
revert to react 17
dai-shi Sep 4, 2021
50f2963
handle error by our own
dai-shi Sep 4, 2021
94d9d4a
v4.0.0-alpha.2
dai-shi Sep 4, 2021
d36611b
fix&refactor a bit
dai-shi Sep 6, 2021
453e59a
Merge branch 'main' into use-sync-external-store
dai-shi Sep 14, 2021
ef89673
update uSES experimental package
dai-shi Sep 14, 2021
d61a0b7
remove error propagation hack
dai-shi Sep 14, 2021
ec117e6
update size snapshot
dai-shi Sep 14, 2021
e2081c8
update uSES and add dts
dai-shi Sep 27, 2021
1fee70e
split react.ts and no export wild
dai-shi Sep 27, 2021
9aaeb5e
split useStore impl
dai-shi Sep 27, 2021
a914052
context to follow the new api, export wild again
dai-shi Sep 27, 2021
9021456
v4.0.0-alpha.3
dai-shi Sep 27, 2021
b9a70f2
add missing await
dai-shi Sep 28, 2021
98c59d3
merge main
dai-shi Sep 28, 2021
c9c8ae9
merge main
dai-shi Oct 1, 2021
cc391bc
update uSES
dai-shi Oct 1, 2021
bef6f81
update uSES
dai-shi Oct 3, 2021
d78e91f
Merge branch 'main' into use-sync-external-store
dai-shi Oct 3, 2021
54a5b8c
uses uSES extra!
dai-shi Oct 4, 2021
66fc5a7
Merge branch 'main' into use-sync-external-store
dai-shi Oct 4, 2021
1ee93aa
v4.0.0-alpha.3
dai-shi Oct 4, 2021
c643ebf
merge main
dai-shi Oct 7, 2021
a20e4d1
merge main
dai-shi Oct 19, 2021
002d101
fix(types): Rename from UseStore to UseBoundStore
dai-shi Oct 19, 2021
674e307
breaking(types): drop deprecated UseStore type
dai-shi Oct 19, 2021
0c641fd
breaking(core): drop v2 hook compatibility
dai-shi Oct 19, 2021
0037eb3
breaking(middleware): drop deprecated persist options
dai-shi Oct 19, 2021
57d2b85
merge main
dai-shi Oct 21, 2021
56d512a
merge main
dai-shi Oct 21, 2021
9b97050
breaking(core): drop deprecated store.subscribe with selector
dai-shi Oct 21, 2021
b691632
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Oct 27, 2021
bedad91
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Oct 27, 2021
263fce4
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Oct 27, 2021
916f0b8
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Oct 27, 2021
3b03362
merge main
dai-shi Oct 27, 2021
24d9750
update uSES
dai-shi Oct 27, 2021
f75e868
fix update uSES
dai-shi Oct 27, 2021
2ed60eb
v4.0.0-alpha.5
dai-shi Oct 27, 2021
3bb2fef
combine subscribe type
dai-shi Oct 29, 2021
d3eec66
merge main
dai-shi Oct 30, 2021
5e062a6
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Oct 30, 2021
4d00841
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Oct 30, 2021
4c80f17
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Oct 30, 2021
9675378
merge main
dai-shi Oct 30, 2021
ba9d6d0
intentional undefined type
dai-shi Oct 31, 2021
8fad667
add useDebugValue
dai-shi Oct 31, 2021
7575357
update uSES
dai-shi Oct 31, 2021
e51f75a
update uSES types
dai-shi Nov 2, 2021
018f9b2
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Nov 2, 2021
9d7ea48
merge main
dai-shi Nov 2, 2021
ebf571e
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Nov 2, 2021
48684d2
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Nov 2, 2021
c001de4
breaking(middleware): make persist options.removeItem required
dai-shi Nov 2, 2021
7941091
merge main
dai-shi Nov 2, 2021
e7adbbf
update uSES
dai-shi Nov 2, 2021
5812d0e
v4.0.0-alpha.6
dai-shi Nov 2, 2021
19d40a6
merge main
dai-shi Nov 4, 2021
b669f7e
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Nov 4, 2021
3357bee
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Nov 4, 2021
dc0b449
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Nov 4, 2021
9690a11
merge main
dai-shi Nov 4, 2021
f037a2a
fix(readme): remove memoization section which is no longer valid with…
dai-shi Nov 4, 2021
8339d52
feat(readme): add new createStore/useStore usage
dai-shi Nov 4, 2021
258e895
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Nov 9, 2021
2392f8f
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Nov 9, 2021
707b37b
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Nov 9, 2021
de84ec8
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Nov 9, 2021
2e5ab21
merge main
dai-shi Nov 9, 2021
37119f5
update useSES
dai-shi Nov 9, 2021
835684a
update uSES and deps
dai-shi Nov 15, 2021
20abedf
Merge branch 'main' into use-sync-external-store
dai-shi Nov 15, 2021
e91bde6
v4.0.0-alpha.7
dai-shi Nov 15, 2021
7206e1a
merge main
dai-shi Nov 16, 2021
e8f7316
merge main
dai-shi Nov 16, 2021
6372ee8
merge main
dai-shi Nov 16, 2021
b5d8665
merge main
dai-shi Nov 16, 2021
c009886
merge main
dai-shi Nov 16, 2021
a0777aa
Merge branch 'breaking/middleware/drop-deprecated-persist-options' in…
dai-shi Nov 16, 2021
8d11b90
Merge branch 'breaking/drop-v2-hook-compatibility' into v4
dai-shi Nov 16, 2021
c972116
Merge branch 'breaking/types/drop-use-store' into v4
dai-shi Nov 16, 2021
cf56bf5
merege uses
dai-shi Nov 16, 2021
e87182b
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Nov 22, 2021
50022f9
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Nov 22, 2021
3c12d7d
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Nov 22, 2021
e675524
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Nov 22, 2021
be18127
merge main
dai-shi Nov 22, 2021
9280262
update uSES
dai-shi Nov 22, 2021
9fb629a
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Dec 3, 2021
e9d2ca3
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Dec 3, 2021
d6a4e42
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Dec 3, 2021
eddbf38
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Dec 3, 2021
c6acc59
merge main
dai-shi Dec 3, 2021
303fd82
update uSES
dai-shi Dec 7, 2021
0d4e0d8
shave bytes
dai-shi Dec 7, 2021
ef5e758
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Dec 7, 2021
eb1e686
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Dec 7, 2021
252fca0
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Dec 7, 2021
eee639e
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Dec 7, 2021
445bc8f
Merge branch 'main' into use-sync-external-store
dai-shi Dec 7, 2021
ba82ac2
Merge branch 'breaking/middleware/drop-deprecated-persist-options' in…
dai-shi Dec 7, 2021
adada31
Merge branch 'breaking/drop-v2-hook-compatibility' into v4
dai-shi Dec 7, 2021
1cf08fa
Merge branch 'breaking/types/drop-use-store' into v4
dai-shi Dec 7, 2021
fd47b39
Merge branch 'use-sync-external-store' into v4
dai-shi Dec 7, 2021
c99035d
Merge branch 'v4' of https://github.com/pmndrs/zustand into v4
devanshj Dec 21, 2021
9d8009a
vanilla: add higher kinded mutator types
devanshj Dec 23, 2021
bcd6e0d
persist: add higher kinded mutator types
devanshj Dec 23, 2021
51ec8cf
persist: try to minimize diff
devanshj Dec 23, 2021
41bf7ea
use `PopArgument` in vanilla too
devanshj Dec 23, 2021
5a6473b
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Dec 24, 2021
3ac128f
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Dec 24, 2021
3818dc1
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Dec 24, 2021
e7a219d
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Dec 24, 2021
dd57cb0
Merge branch 'main' into use-sync-external-store
dai-shi Dec 24, 2021
76b1d4c
update uSES
dai-shi Dec 24, 2021
923c46f
Merge branch 'breaking/middleware/drop-deprecated-persist-options' in…
dai-shi Dec 24, 2021
be26ceb
Merge branch 'breaking/drop-v2-hook-compatibility' into v4
dai-shi Dec 24, 2021
d59dde1
Merge branch 'breaking/types/drop-use-store' into v4
dai-shi Dec 24, 2021
33d5940
Merge branch 'use-sync-external-store' into v4
dai-shi Dec 24, 2021
847334d
use overloads instead of `createWithState`
devanshj Jan 6, 2022
3e1beaa
avoid symbols
devanshj Jan 8, 2022
15b478f
add new types to middlewares
devanshj Jan 8, 2022
1bdaa39
add new types react
devanshj Jan 8, 2022
5eeeb3c
add new types to context
devanshj Jan 8, 2022
dcfb92f
fix persist types
devanshj Jan 8, 2022
b0ea17f
add immer
devanshj Jan 8, 2022
45b4317
migrate middleware type tests
devanshj Jan 8, 2022
40d8ce8
fix react type, export `UseBoundStore`
devanshj Jan 8, 2022
308112a
migrate vanilla type tests
devanshj Jan 8, 2022
9e2826b
merge v4
devanshj Jan 9, 2022
272e254
rename `_createStore` to `createStoreImpl`
devanshj Jan 9, 2022
c61e207
Default to no mutations in `StateCreator`
devanshj Jan 16, 2022
34d13b5
migrate context.test.tsx
devanshj Feb 9, 2022
9238443
fix devtools.test.tsx type erros
devanshj Feb 9, 2022
1bf8afd
context: remove callsignature in useStoreApi
devanshj Feb 9, 2022
b95fb26
context: remove `UseContextStore` type
devanshj Feb 9, 2022
cffa52a
context: fix useBoundStore type
devanshj Feb 9, 2022
5aa4bac
context: keep `UseContextStore` for tooltip just don't export it
devanshj Feb 9, 2022
594c7fa
react: remove duplicate overload in create
devanshj Feb 9, 2022
d38a259
export `WithPersist`
devanshj Feb 9, 2022
7a1ec6b
devtools: preserve try/catch
devanshj Feb 9, 2022
045ddb9
devtools: preserve window check
devanshj Feb 9, 2022
58d06bc
add a test case for v3 style create
devanshj Feb 9, 2022
5c2ac1a
devtools: preverse test fix from base branch
devanshj Feb 9, 2022
14ded44
remove StoreApiWithFoo types, don't export WithFoo types
devanshj Feb 9, 2022
0fac7b5
style
devanshj Feb 10, 2022
e2f4c74
devtools: preverse `originalIsRecording` change
devanshj Feb 10, 2022
970b0e2
fix bug in devtools
dai-shi Feb 11, 2022
c037c08
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Feb 11, 2022
bbbf857
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Feb 11, 2022
d3810d1
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Feb 11, 2022
82856c5
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Feb 11, 2022
90fd7ee
Merge branch 'main' into use-sync-external-store
dai-shi Feb 11, 2022
ad3b466
Merge branch 'breaking/middleware/drop-deprecated-persist-options' in…
dai-shi Feb 11, 2022
a0f186e
Merge branch 'breaking/drop-v2-hook-compatibility' into v4
dai-shi Feb 11, 2022
bc1e120
Merge branch 'breaking/types/drop-use-store' into v4
dai-shi Feb 11, 2022
742f76d
Merge branch 'use-sync-external-store' into v4
dai-shi Feb 11, 2022
8031d3f
empty commit
dai-shi Feb 11, 2022
ac2dcaa
4.0.0-beta.1
dai-shi Feb 11, 2022
01b42df
fix lint
dai-shi Feb 11, 2022
4e162d0
style
devanshj Feb 13, 2022
406b104
merge v4 branch
devanshj Feb 13, 2022
3d6d26f
export immer fix tests
devanshj Feb 13, 2022
36a92f7
style, minor fixes
devanshj Feb 17, 2022
82cc93a
devtools: fix test
devanshj Feb 17, 2022
3687f68
Update tests/devtools.test.tsx
dai-shi Feb 18, 2022
be51c33
breaking(middleware/devtools): use official devtools extension types
dai-shi Feb 22, 2022
06123b1
type object.create
dai-shi Feb 23, 2022
e9cf15e
avoid emitting @redux-devtools/extension
dai-shi Feb 23, 2022
4a30a95
fix type with any
dai-shi Feb 24, 2022
86d4f97
refactor
dai-shi Feb 24, 2022
4241357
Merge branch 'main' into breaking/middleware/devtools/use-devtools-ex…
dai-shi Feb 28, 2022
9b8fe3e
Merge branch 'main' into breaking/core/drop-deprecated-subscribe-with…
dai-shi Feb 28, 2022
5298a35
Merge branch 'main' into breaking/middleware/drop-deprecated-persist-…
dai-shi Feb 28, 2022
5ec8123
Merge branch 'main' into breaking/drop-v2-hook-compatibility
dai-shi Feb 28, 2022
37c4430
Merge branch 'main' into breaking/types/drop-use-store
dai-shi Feb 28, 2022
ff7358a
Merge branch 'main' into use-sync-external-store
dai-shi Feb 28, 2022
92a7094
fix yarn lock
dai-shi Feb 28, 2022
64536ee
temporary fix #829
dai-shi Feb 28, 2022
fede093
Merge branch 'breaking/core/drop-deprecated-subscribe-with-selector' …
dai-shi Feb 28, 2022
4be2722
Merge branch 'breaking/middleware/drop-deprecated-persist-options' in…
dai-shi Feb 28, 2022
12ab776
Merge branch 'breaking/drop-v2-hook-compatibility' into v4
dai-shi Feb 28, 2022
c79d27c
Merge branch 'breaking/types/drop-use-store' into v4
dai-shi Feb 28, 2022
d5f7fa7
Merge branch 'use-sync-external-store' into v4
dai-shi Feb 28, 2022
0c0f3a3
v4.0.0-beta.2
dai-shi Feb 28, 2022
304ee89
fix lint
dai-shi Feb 28, 2022
d4b7200
lock date-fns version
dai-shi Mar 4, 2022
48b0267
test middleware subtyping
devanshj Mar 5, 2022
d0820ee
Merge branch 'add-hkm-types' of https://github.com/devanshj/zustand i…
devanshj Mar 5, 2022
1ffbd34
merge v4
devanshj Mar 5, 2022
b653b4b
fix errors in conflict resolution
devanshj Mar 5, 2022
129b64b
lock testing-library/react alpha version
dai-shi Mar 5, 2022
49f0a3a
Merge branch 'v4' of https://github.com/pmndrs/zustand into add-hkm-t…
devanshj Mar 20, 2022
7f54b3c
more correct (and strict) persist types
devanshj Mar 20, 2022
414c398
migrate tests
devanshj Mar 20, 2022
155a177
wip release notes
devanshj Mar 21, 2022
b76e36c
fix devtools merge with base
devanshj Mar 27, 2022
719d184
add a test case for persist with partialize option
devanshj Mar 27, 2022
6198b31
Merge branch 'add-hkm-types' of https://github.com/devanshj/zustand i…
devanshj Mar 27, 2022
c71afb6
update readme
devanshj Mar 30, 2022
826ef8e
fix lint
devanshj Mar 30, 2022
2873f22
immer: mutate `store.setState`
devanshj Mar 30, 2022
a539f54
fix devtools merge with base
devanshj Mar 30, 2022
26f04d4
immer: fix mutations order
devanshj Apr 3, 2022
968ca5d
changes in readme
devanshj Apr 3, 2022
7b4793f
move and rename v4 migration md
devanshj Apr 3, 2022
d635082
add `combine` usage in readme
devanshj Apr 3, 2022
810b418
typos
devanshj Apr 3, 2022
4f9d13f
create separate md for typescript, add common recipes
devanshj Apr 4, 2022
5424719
minor fixes
devanshj Apr 4, 2022
cb8c4f3
devtools: minor type fix
devanshj Apr 4, 2022
a328e04
add more migrations
devanshj Apr 4, 2022
57078dd
context: fix import
devanshj Apr 5, 2022
25bdc48
devtools: minor refactor
devanshj Apr 5, 2022
269638b
Merge branch 'add-hkm-types' of https://github.com/devanshj/zustand i…
devanshj Apr 5, 2022
1f2041c
fix lint: run prettier
devanshj Apr 5, 2022
e927935
wip
devanshj Apr 5, 2022
7aaac47
uSES rc.1
dai-shi Mar 4, 2022
5a0b404
getServerState for #886, no types yet
dai-shi Mar 31, 2022
1a13c15
uSES v1
dai-shi Apr 5, 2022
87da744
devtools: remove deprecations and warnings
devanshj Apr 5, 2022
a01cf35
fix tests
devanshj Apr 5, 2022
dec16dc
v4.0.0-beta.2
dai-shi Apr 5, 2022
ba0660e
merge base
devanshj Apr 6, 2022
8cad3f6
wip
devanshj Apr 8, 2022
93aff9b
merge base ie main
devanshj Apr 8, 2022
aa1c0e2
migrate tests
devanshj Apr 8, 2022
ba702c1
persist: keep diff minimal
devanshj Apr 8, 2022
ee512ec
fix merge in package.json and yarn.lock
dai-shi Apr 8, 2022
efecba0
fix merge for persist
devanshj Apr 8, 2022
865f60b
Merge branch 'add-hkm-types' of https://github.com/devanshj/zustand i…
devanshj Apr 8, 2022
0a4625b
don't use `import type`
devanshj Apr 9, 2022
160f11d
docs(typescript): add slices pattern
devanshj Apr 9, 2022
46589cc
Merge branch 'main' into add-hkm-types
dai-shi Apr 11, 2022
6ed70c3
fix selector & equals types
devanshj Apr 11, 2022
abf876a
Merge branch 'add-hkm-types' of https://github.com/devanshj/zustand i…
devanshj Apr 11, 2022
061a057
add test for setState with replace
devanshj Apr 11, 2022
5477b3d
remove undefined selector overload
devanshj Apr 12, 2022
f0d192a
make immer more generic
devanshj Apr 11, 2022
58dc684
make devtools replace-friendly and more correctly typed
devanshj Apr 11, 2022
374a5f0
migrate tests
devanshj Apr 12, 2022
3063df1
make setState bivariant to make the state covariant
devanshj Apr 12, 2022
8a3b5db
devtools: return the result of `setState`
devanshj Apr 13, 2022
c4a6fe5
devtools: make the fallback branch in `StoreSetStateWithAction` bivar…
devanshj Apr 14, 2022
fb624ac
remove strict replace
devanshj Apr 14, 2022
4861161
fix lint
devanshj Apr 17, 2022
9d6e7e2
Merge branch 'main' into add-hkm-types
dai-shi Apr 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
382 changes: 382 additions & 0 deletions docs/typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,382 @@
# TypeScript Guide

## Basic usage

When using TypeScript you just have to make a tiny change that instead of writing `create(...)` you'll have to write `create<T>()(...)` where `T` would be type of the state so as to annotate it. Example...

```ts
import create from "zustand"

interface BearState {
bears: number
increase: (by: number) => void
}

const useStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))
```

<details>
<summary>Why can't we just simply infer the type from initial state?</summary>

**TLDR**: Because state generic `T` is invariant.

Consider this minimal version `create`...

```ts
declare const create: <T>(f: (get: () => T) => T) => T

const x = create((get) => ({
foo: 0,
bar: () => get()
}))
// `x` is inferred as `unknown` instead of
// interface X {
// foo: number,
// bar: () => X
// }
```

Here if you look at the type of `f` in `create` ie `(get: () => T) => T` it "gives" `T` as it returns `T` but then it also "takes" `T` via `get` so where does `T` come from TypeScript thinks... It's a like that chicken or egg problem. At the end TypeScript gives up and infers `T` as `unknown`.

So as long as the generic to be inferred is invariant TypeScript won't be able to infer it. Another simple example would be this...

```ts
declare const createFoo: <T>(f: (t: T) => T) => T
const x = createFoo(_ => "hello")
```

Here again `x` is `unknown` instead of `string`.

Now one can argue it's impossible to write an implementation for `createFoo`, and that's true. But then it's also impossible to write Zustand's `create`... Wait but Zustand exists? So what do I mean by that?

The thing is Zustand is lying in it's type, the simplest way to prove it by showing unsoundness. Consider this example...

```ts
import create from "zustand/vanilla"

const useStore = create<{ foo: number }>()((_, get) => ({
foo: get().foo,
}))
```

This code compiles, but guess what happens when you run it? You'll get an exception "Uncaught TypeError: Cannot read properties of undefined (reading 'foo') because after all `get` would return `undefined` before the initial state is created (hence kids don't call `get` when creating the initial state). But the types tell that get is `() => { foo: number }` which is exactly the lie I was taking about, `get` is that eventually but first it's `() => undefined`.

Okay we're quite deep in the rabbit hole haha, long story short zustand has a bit crazy runtime behavior that can't be typed in a sound way and inferrable way. We could make it inferrable with the right TypeScript features that don't exist today. And hey that tiny bit of unsoundness is not a problem.
</details>

<details>
<summary>Why that currying `()(...)`?</summary>

**TLDR**: It's a workaround for [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571).

Imagine you have a scenario like this...

```ts
declare const withError: <T, E>(p: Promise<T>) =>
Promise<[error: undefined, value: T] | [error: E, value: undefined]>
declare const doSomething: () => Promise<string>

const main = async () => {
let [error, value] = await withError(doSomething())
}
```

Here `T` is inferred as `string` and `E` is inferred as `unknown`. Now for some reason you want to annotate `E` as `Foo` because you're certain what shape of error `doSomething()` would throw. But too bad you can't do that, you can either pass all generics or none. So now along with annotating `E` as `Foo` you'll also have to annotate `T` as `string` which gets inferred anyway. So what to do? What you do is make a curried version of `withError` that does nothing in runtime, it's purpose is to just allow you annotate `E`...

```ts
declare const withError: {
<E>(): <T>(p: Promise<T>) =>
Promise<[error: undefined, value: T] | [error: E, value: undefined]>
<T, E>(p: Promise<T>):
Promise<[error: undefined, value: T] | [error: E, value: undefined]>
}
declare const doSomething: () => Promise<string>
interface Foo { bar: string }

const main = async () => {
let [error, value] = await withError<Foo>()(doSomething())
}
```

And now `T` gets inferred and you get to annotate `E` too. Zustand has the same use case we want to annotate the state (the first type parameter) but allow the rest type parameters to get inferred.
</details>

Alternatively you can also use `combine` which infers the state instead of you having to type it...

```ts
import create from "zustand"
import { combine } from "zustand/middleware"

const useStore = create(combine({ bears: 0 }, (set) => ({
increase: (by: number) => set((state) => ({ bears: state.bears + by })),
}))
```

<details>
<summary>But be a little careful...</summary>

We achieve the inference by lying a little in the types of `set`, `get` and `store` that you receive as parameters. The lie is that they're typed in a way as if the state is the first parameter only when in fact the state is the shallow-merge (`{ ...a, ...b }`) of both first parameter and the second parameter's return. So for example `get` from the second parameter has type `() => { bears: number }` and that's a lie as it should be `() => { bears: number, increase: (by: number) => void }`. And `useStore` still has the correct type, ie for example `useStore.getState` is typed as `() => { bears: number, increase: (by: number) => void }`.

It's not a lie lie because `{ bears: number }` is still a subtype `{ bears: number, increase: (by: number) => void }`, so in most cases there won't be a problem. Just you have to be careful while using replace. For eg `set({ bears: 0 }, true)` would compile but will be unsound as it'll delete the `increase` function. (If you set from "outside" ie `useStore.setState({ bears: 0 }, true)` then it won't compile because the "outside" store knows that `increase` is missing.) Another instance where you should be careful you're doing `Object.keys`, `Object.keys(get())` will return `["bears", "increase"]` and not `["bears"]` (the return type of `get` can make you fall for this).

So `combine` trades-off a little type-safety for the convience of not having to write a type for state. Hence you should use `combine` accordingly, usually it's not a big deal and it's okay to use it.
</details>

## Using middlewares

You don't have to do anything special to use middlewares in TypeScript.

```ts
import create from "zustand"
import { devtools, persist } from "zustand/middleware"

interface BearState {
bears: number
increase: (by: number) => void
}

const useStore = create<BearState>()(devtools(persist((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))))
```

Just make sure you're using them immediately inside `create` so as to make the contextual inference work. Doing something even remotely fancy like the following `myMiddlewares` would require more advanced types.

```ts
import create from "zustand"
import { devtools, persist } from "zustand/middleware"

const myMiddlewares = f => devtools(persist(f))

interface BearState {
bears: number
increase: (by: number) => void
}

const useStore = create<BearState>()(myMiddlewares((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
})))
```

## Authoring middlewares and advanced usage

Imagine you had to write this hypothetical middleware...

```js
import create from "zustand"

const foo = (f, bar) => (set, get, store) => {
store.foo = bar
return f(set, get, store);
}

const useStore = create(foo(() => ({ bears: 0 }), "hello"))
console.log(store.foo.toUpperCase())
```

Yes, if you didn't know Zustand middlewares do and are allowed to mutate the store. But how could we possibly encode the mutation on the type-level? That is to say how could do we type `foo` so that this code compiles?

For an usual statically typed language this is impossible, but thanks to TypeScript, Zustand has something called an "higher kinded mutator" that makes this possible. If you're dealing with complex type problems like typing a middleware or using the `StateCreator` type, then you'll have to understand this implementation detail, for that check out [#710](https://github.com/pmndrs/zustand/issues/710).

If you're eager to know what the answer is to this particular problem then it's [here](#middleware-that-changes-the-store-type).

## Common recipes

### Middleware that does not change the store type

```ts
import create, { State, StateCreator, StoreMutatorIdentifier, Mutate, StoreApi } from "zustand"

type Logger =
< T extends State
, Mps extends [StoreMutatorIdentifier, unknown][] = []
, Mcs extends [StoreMutatorIdentifier, unknown][] = []
>
( f: StateCreator<T, Mps, Mcs>
, name?: string
) =>
StateCreator<T, Mps, Mcs>

type LoggerImpl =
<T extends State>
( f: PopArgument<StateCreator<T, [], []>>
, name?: string
) =>
PopArgument<StateCreator<T, [], []>>

const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => {
type T = ReturnType<typeof f>
const loggedSet: typeof set = (...a) => {
set(...a)
console.log(...(name ? [`${name}:`] : []), get())
}
store.setState = loggedState

return f(loggedSet, get, store)
}

export const logger = loggerImpl as unknown as Foo

type PopArgument<T extends (...a: never[]) => unknown> =
T extends (...a: [...infer A, infer _]) => infer R
? (...a: A) => R
: never

// ---

const useStore = create<BearState>()(logger((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}), "bear-store"))
```

### Middleware that changes the store type

```js
import create, { State, StateCreator, StoreMutatorIdentifier, Mutate, StoreApi } from "zustand"

type Foo =
< T extends State
, A
, Mps extends [StoreMutatorIdentifier, unknown][] = []
, Mcs extends [StoreMutatorIdentifier, unknown][] = []
>
( f: StateCreator<T, [...Mps, ['foo', A]], Mcs>
, bar: A
) =>
StateCreator<T, Mps, [['foo', A], ...Mcs]>

declare module 'zustand' {
interface StoreMutators<S, A> {
foo: Write<Cast<S, object> { foo: A }>
}
}

type FooImpl =
<T extends State, A>
( f: PopArgument<StateCreator<T, [], []>>
, bar: A
) => PopArgument<StateCreator<T, [], []>>

const fooImpl: FooImpl = (f, bar) => (set, get, _store) => {
type T = ReturnType<typeof f>
type A = typeof bar

const store = _store as Mutate<StoreApi<T>, [['foo', A]]>
store.foo = bar
return f(set, get, _store)
}

export const foo = fooImpl as unknown as Foo

type PopArgument<T extends (...a: never[]) => unknown> =
T extends (...a: [...infer A, infer _]) => infer R
? (...a: A) => R
: never

type Write<T extends object, U extends object> =
Omit<T, keyof U> & U

type Cast<T, U> =
T extends U ? T : U;

// ---

const useStore = create(foo(() => ({ bears: 0 }), "hello"))
console.log(store.foo.toUpperCase())
```

### `create` without curried workaround

The recommended way to use `create` is using the curried workaround ie `create<T>()(...)` because this enabled you to infer the store type. But for some reason if you don't want to use the workaround then you can pass the type parameters like the following. Note that in some cases this acts as an assertion instead of annotation, so it's not recommended.

```ts
import create, { State, StateCreator, StoreMutatorIdentifier, Mutate, StoreApi } from "zustand"

interface BearState {
bears: number
increase: (by: number) => void
}

const useStore = create<
BearState,
[
['zustand/persist', BearState],
['zustand/devtools', never]
]
>(devtools(persist((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
})))
```

### Independent slices pattern

```ts
import create, { State, StateCreator, StoreMutatorIdentifier, Mutate, StoreApi } from "zustand"

interface BearSlice {
bears: number
addBear: () => void
}
const createBearSlice: StateCreator<BearSlice, [], []> = (set) => ({
bears: 0,
addBear: () => set((state) => ({ bears: state.bears + 1 }))
})

interface FishSlice {
fishes: number
addFish: () => void
}
const createFishSlice: StateCreator<FishSlice, [], []> = (set) => ({
fishes: 0,
addFish: () => set((state) => ({ fishes: state.fishes + 1 }))
})

const useStore = create<BearSlice & FishSlice>()((...a) => ({
...createBearSlice(...a),
...createFishSlice(...a)
}))
```

If you have some middlewares then replace `StateCreator<MySlice, [], []>` with `StateCreator<MySlice, Mutators, []>`. Eg if you're using `devtools` then it'll be `StateCreator<MySlice, [["zustand/devtools", never]], []>`.

Also you can even write `StateCreator<MySlice>` instead of `StateCreator<MySlice, [], []>` as the second and third parameter have `[]` as their default value.

### Interdependent slices pattern

```ts
import create, { State, StateCreator, StoreMutatorIdentifier, Mutate, StoreApi } from "zustand"

interface BearSlice {
bears: number
addBear: () => void
eatFish: () => void
}
const createBearSlice: StateCreator<BearSlice & FishSlice, [], [], BearSlice> = (set) => ({
bears: 0,
addBear: () => set((state) => ({ bears: state.bears + 1 })),
eatFish: () => set((state) => ({ fishes: state.fishes - 1 }))
})

interface FishSlice {
fishes: number
addFish: () => void
}
const createFishSlice: StateCreator<BearSlice & FishSlice, [], [], FishSlice> = (set) => ({
fishes: 0,
addFish: () => set((state) => ({ fishes: state.fishes + 1 }))
})

const useStore = create<BearSlice & FishSlice>()((...a) => ({
...createBearSlice(...a),
...createFishSlice(...a)
}))
```

If you have some middlewares then replace `StateCreator<MyState, [], [], MySlice>` with `StateCreator<MyState, Mutators, [], MySlice>`. Eg if you're using `devtools` then it'll be `StateCreator<MyState, [["zustand/devtools", never]], [], MySlice>`.
Loading