From ca059788d3f015b1582dc4d82b2bd4dbb6d93de4 Mon Sep 17 00:00:00 2001 From: Devansh Jethmalani Date: Sun, 23 Oct 2022 05:25:31 +0530 Subject: [PATCH] fix(types): fix `StateCreator` subtyping (#1373) * fix(types): fix `StateCreator` subtyping * prefer import type Co-authored-by: daishi --- docs/guides/typescript.md | 20 ++++---------------- src/middleware/devtools.ts | 10 ++-------- src/middleware/immer.ts | 10 ++-------- src/middleware/persist.ts | 10 ++-------- src/middleware/redux.ts | 10 ++-------- src/middleware/subscribeWithSelector.ts | 10 ++-------- src/vanilla.ts | 15 ++------------- tests/types.test.tsx | 22 +++++++++++++++++++++- 8 files changed, 37 insertions(+), 70 deletions(-) diff --git a/docs/guides/typescript.md b/docs/guides/typescript.md index e750b18111..4403064235 100644 --- a/docs/guides/typescript.md +++ b/docs/guides/typescript.md @@ -246,9 +246,9 @@ type Logger = < ) => StateCreator type LoggerImpl = ( - f: PopArgument>, + f: StateCreator, name?: string -) => PopArgument> +) => StateCreator const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => { type T = ReturnType @@ -263,12 +263,6 @@ const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => { export const logger = loggerImpl as unknown as Logger -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never - // --- const useBearStore = create()( @@ -310,9 +304,9 @@ declare module 'zustand' { } type FooImpl = ( - f: PopArgument>, + f: StateCreator, bar: A -) => PopArgument> +) => StateCreator const fooImpl: FooImpl = (f, bar) => (set, get, _store) => { type T = ReturnType @@ -325,12 +319,6 @@ const fooImpl: FooImpl = (f, bar) => (set, get, _store) => { export const foo = fooImpl as unknown as Foo -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never - type Write = Omit & U type Cast = T extends U ? T : U diff --git a/src/middleware/devtools.ts b/src/middleware/devtools.ts index 287b7a2b71..1c6641d812 100644 --- a/src/middleware/devtools.ts +++ b/src/middleware/devtools.ts @@ -141,15 +141,9 @@ declare module '../vanilla' { } type DevtoolsImpl = ( - storeInitializer: PopArgument>, + storeInitializer: StateCreator, devtoolsOptions?: DevtoolsOptions -) => PopArgument> - -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never +) => StateCreator export type NamedSet = WithDevtools>['setState'] diff --git a/src/middleware/immer.ts b/src/middleware/immer.ts index ee661595bc..9faf7da147 100644 --- a/src/middleware/immer.ts +++ b/src/middleware/immer.ts @@ -49,15 +49,9 @@ type StoreImmer = S extends { : never : never -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never - type ImmerImpl = ( - storeInitializer: PopArgument> -) => PopArgument> + storeInitializer: StateCreator +) => StateCreator const immerImpl: ImmerImpl = (initializer) => (set, get, store) => { type T = ReturnType diff --git a/src/middleware/persist.ts b/src/middleware/persist.ts index 20fa625c08..53c4cda2b1 100644 --- a/src/middleware/persist.ts +++ b/src/middleware/persist.ts @@ -319,14 +319,8 @@ type WithPersist = S extends { getState: () => infer T } : never type PersistImpl = ( - storeInitializer: PopArgument>, + storeInitializer: StateCreator, options: PersistOptions -) => PopArgument> - -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never +) => StateCreator export const persist = persistImpl as unknown as Persist diff --git a/src/middleware/redux.ts b/src/middleware/redux.ts index 3735116201..890888becb 100644 --- a/src/middleware/redux.ts +++ b/src/middleware/redux.ts @@ -31,16 +31,10 @@ declare module '../vanilla' { } } -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never - type ReduxImpl = ( reducer: (state: T, action: A) => T, initialState: T -) => PopArgument, [], []>> +) => StateCreator, [], []> const reduxImpl: ReduxImpl = (reducer, initial) => (set, _get, api) => { type S = typeof initial @@ -53,4 +47,4 @@ const reduxImpl: ReduxImpl = (reducer, initial) => (set, _get, api) => { return { dispatch: (...a) => (api as any).dispatch(...a), ...initial } } -export const redux = reduxImpl as Redux +export const redux = reduxImpl as unknown as Redux diff --git a/src/middleware/subscribeWithSelector.ts b/src/middleware/subscribeWithSelector.ts index 69e6d0585d..19dbdcb1a7 100644 --- a/src/middleware/subscribeWithSelector.ts +++ b/src/middleware/subscribeWithSelector.ts @@ -40,14 +40,8 @@ type StoreSubscribeWithSelector = { } type SubscribeWithSelectorImpl = ( - storeInitializer: PopArgument> -) => PopArgument> - -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never + storeInitializer: StateCreator +) => StateCreator const subscribeWithSelectorImpl: SubscribeWithSelectorImpl = (fn) => (set, get, api) => { diff --git a/src/vanilla.ts b/src/vanilla.ts index b8cddf5633..315b07c6b8 100644 --- a/src/vanilla.ts +++ b/src/vanilla.ts @@ -30,8 +30,7 @@ export type StateCreator< > = (( setState: Get, Mis>, 'setState', never>, getState: Get, Mis>, 'getState', never>, - store: Mutate, Mis>, - $$storeMutations: Mis + store: Mutate, Mis> ) => U) & { $$storeMutators?: Mos } // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface @@ -55,12 +54,6 @@ type CreateStoreImpl = < initializer: StateCreator ) => Mutate, Mos> -type PopArgument unknown> = T extends ( - ...a: [...infer A, infer _] -) => infer R - ? (...a: A) => R - : never - const createStoreImpl: CreateStoreImpl = (createState) => { type TState = ReturnType type Listener = (state: TState, prevState: TState) => void @@ -94,11 +87,7 @@ const createStoreImpl: CreateStoreImpl = (createState) => { const destroy: () => void = () => listeners.clear() const api = { setState, getState, subscribe, destroy } - state = (createState as PopArgument)( - setState, - getState, - api - ) + state = createState(setState, getState, api) return api as any } diff --git a/tests/types.test.tsx b/tests/types.test.tsx index 7335672161..48df3da752 100644 --- a/tests/types.test.tsx +++ b/tests/types.test.tsx @@ -1,4 +1,5 @@ -import create, { +import create from 'zustand' +import type { StateCreator, StoreApi, StoreMutatorIdentifier, @@ -213,3 +214,22 @@ it('StateCreator is StateCreator', create()(persist(foo())) }) + +it('StateCreator subtyping', () => { + interface State { + count: number + increment: () => void + } + + const foo: () => StateCreator = () => (set, get) => ({ + count: 0, + increment: () => { + set({ count: get().count + 1 }) + }, + }) + + create()(persist(foo())) + + const _testSubtyping: StateCreator = + {} as StateCreator +})