mirror of
https://github.com/pmndrs/zustand.git
synced 2025-12-08 19:45:52 +00:00
fix(types): avoid using deprecated types (#1122)
* fix(types): avoid using deprecated types * simplify a bit
This commit is contained in:
parent
0938b17a77
commit
49dcec03ee
@ -6,19 +6,13 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import {
|
||||
EqualityChecker,
|
||||
State,
|
||||
StateSelector,
|
||||
StoreApi,
|
||||
useStore,
|
||||
} from 'zustand'
|
||||
import { StoreApi, useStore } from 'zustand'
|
||||
|
||||
type UseContextStore<S extends StoreApi<State>> = {
|
||||
type UseContextStore<S extends StoreApi> = {
|
||||
(): ExtractState<S>
|
||||
<U>(
|
||||
selector: StateSelector<ExtractState<S>, U>,
|
||||
equalityFn?: EqualityChecker<U>
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equalityFn?: (a: U, b: U) => boolean
|
||||
): U
|
||||
}
|
||||
|
||||
@ -26,7 +20,7 @@ type ExtractState<S> = S extends { getState: () => infer T } ? T : never
|
||||
|
||||
type WithoutCallSignature<T> = { [K in keyof T]: T[K] }
|
||||
|
||||
function createContext<S extends StoreApi<State>>() {
|
||||
function createContext<S extends StoreApi>() {
|
||||
const ZustandContext = reactCreateContext<S | undefined>(undefined)
|
||||
|
||||
const Provider = ({
|
||||
@ -50,8 +44,8 @@ function createContext<S extends StoreApi<State>>() {
|
||||
}
|
||||
|
||||
const useBoundStore = (<StateSlice = ExtractState<S>>(
|
||||
selector?: StateSelector<ExtractState<S>, StateSlice>,
|
||||
equalityFn?: EqualityChecker<StateSlice>
|
||||
selector?: (state: ExtractState<S>) => StateSlice,
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
||||
) => {
|
||||
const store = useContext(ZustandContext)
|
||||
if (!store) {
|
||||
@ -61,7 +55,7 @@ function createContext<S extends StoreApi<State>>() {
|
||||
}
|
||||
return useStore(
|
||||
store,
|
||||
selector as StateSelector<ExtractState<S>, StateSlice>,
|
||||
selector as (state: ExtractState<S>) => StateSlice,
|
||||
equalityFn
|
||||
)
|
||||
}) as UseContextStore<S>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { State, StateCreator, StoreMutatorIdentifier } from '../vanilla'
|
||||
import { StateCreator, StoreMutatorIdentifier } from '../vanilla'
|
||||
|
||||
type Write<T, U> = Omit<T, keyof U> & U
|
||||
|
||||
type Combine = <
|
||||
T extends State,
|
||||
U extends State,
|
||||
T extends object,
|
||||
U extends object,
|
||||
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = []
|
||||
>(
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
import type {} from '@redux-devtools/extension'
|
||||
import {
|
||||
PartialState,
|
||||
SetState,
|
||||
State,
|
||||
StateCreator,
|
||||
StoreApi,
|
||||
StoreMutatorIdentifier,
|
||||
} from '../vanilla'
|
||||
import { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla'
|
||||
|
||||
declare module '../vanilla' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@ -76,7 +69,7 @@ export interface DevtoolsOptions {
|
||||
}
|
||||
|
||||
type Devtools = <
|
||||
T extends State,
|
||||
T extends object,
|
||||
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = []
|
||||
>(
|
||||
@ -91,7 +84,7 @@ declare module '../vanilla' {
|
||||
}
|
||||
}
|
||||
|
||||
type DevtoolsImpl = <T extends State>(
|
||||
type DevtoolsImpl = <T extends object>(
|
||||
storeInitializer: PopArgument<StateCreator<T, [], []>>,
|
||||
devtoolsOptions?: DevtoolsOptions
|
||||
) => PopArgument<StateCreator<T, [], []>>
|
||||
@ -102,12 +95,13 @@ type PopArgument<T extends (...a: never[]) => unknown> = T extends (
|
||||
? (...a: A) => R
|
||||
: never
|
||||
|
||||
export type NamedSet<T extends State> = WithDevtools<StoreApi<T>>['setState']
|
||||
export type NamedSet<T extends object> = WithDevtools<StoreApi<T>>['setState']
|
||||
|
||||
const devtoolsImpl: DevtoolsImpl =
|
||||
(fn, devtoolsOptions = {}) =>
|
||||
(set, get, api) => {
|
||||
type S = ReturnType<typeof fn>
|
||||
type PartialState = Partial<S> | ((s: S) => Partial<S>)
|
||||
|
||||
const { enabled, anonymousActionType, ...options } = devtoolsOptions
|
||||
let extensionConnector:
|
||||
@ -145,7 +139,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
)
|
||||
return r
|
||||
}
|
||||
const setStateFromDevtools: SetState<S> = (...a) => {
|
||||
const setStateFromDevtools: StoreApi<S>['setState'] = (...a) => {
|
||||
const originalIsRecording = isRecording
|
||||
isRecording = false
|
||||
set(...a)
|
||||
@ -193,11 +187,11 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
)
|
||||
return
|
||||
}
|
||||
return parseJsonThen<{ type: unknown; state?: PartialState<S> }>(
|
||||
return parseJsonThen<{ type: unknown; state?: PartialState }>(
|
||||
message.payload,
|
||||
(action) => {
|
||||
if (action.type === '__setState') {
|
||||
setStateFromDevtools(action.state as PartialState<S>)
|
||||
setStateFromDevtools(action.state as PartialState)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// eslint-disable-next-line import/named
|
||||
import { Draft, produce } from 'immer'
|
||||
import { State, StateCreator, StoreMutatorIdentifier } from '../vanilla'
|
||||
import { StateCreator, StoreMutatorIdentifier } from '../vanilla'
|
||||
|
||||
type Immer = <
|
||||
T extends State,
|
||||
T extends object,
|
||||
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = []
|
||||
>(
|
||||
@ -56,7 +56,7 @@ type PopArgument<T extends (...a: never[]) => unknown> = T extends (
|
||||
? (...a: A) => R
|
||||
: never
|
||||
|
||||
type ImmerImpl = <T extends State>(
|
||||
type ImmerImpl = <T extends object>(
|
||||
storeInitializer: PopArgument<StateCreator<T, [], []>>
|
||||
) => PopArgument<StateCreator<T, [], []>>
|
||||
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
State,
|
||||
StateCreator,
|
||||
StoreApi,
|
||||
StoreMutatorIdentifier,
|
||||
} from '../vanilla'
|
||||
import { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla'
|
||||
|
||||
export interface StateStorage {
|
||||
getItem: (name: string) => string | null | Promise<string | null>
|
||||
@ -36,7 +31,7 @@ export interface PersistOptions<S, PersistedState = S> {
|
||||
serialize?: (state: StorageValue<S>) => string | Promise<string>
|
||||
/**
|
||||
* Use a custom deserializer.
|
||||
* Must return an object matching StorageValue<State>
|
||||
* Must return an object matching StorageValue<S>
|
||||
*
|
||||
* @param str The storage's current value.
|
||||
* @default JSON.parse
|
||||
@ -77,7 +72,7 @@ export interface PersistOptions<S, PersistedState = S> {
|
||||
|
||||
type PersistListener<S> = (state: S) => void
|
||||
|
||||
type StorePersist<S extends State, Ps> = {
|
||||
type StorePersist<S extends object, Ps> = {
|
||||
persist: {
|
||||
setOptions: (options: Partial<PersistOptions<S, Ps>>) => void
|
||||
clearStorage: () => void
|
||||
@ -302,7 +297,7 @@ const persistImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
}
|
||||
|
||||
type Persist = <
|
||||
T extends State,
|
||||
T extends object,
|
||||
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
U = T
|
||||
@ -321,10 +316,10 @@ type Write<T extends object, U extends object> = Omit<T, keyof U> & U
|
||||
type Cast<T, U> = T extends U ? T : U
|
||||
|
||||
type WithPersist<S, A> = S extends { getState: () => infer T }
|
||||
? Write<S, StorePersist<Cast<T, State>, A>>
|
||||
? Write<S, StorePersist<Cast<T, object>, A>>
|
||||
: never
|
||||
|
||||
type PersistImpl = <T extends State>(
|
||||
type PersistImpl = <T extends object>(
|
||||
storeInitializer: PopArgument<StateCreator<T, [], []>>,
|
||||
options: PersistOptions<T, T>
|
||||
) => PopArgument<StateCreator<T, [], []>>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { State, StateCreator, StoreMutatorIdentifier } from '../vanilla'
|
||||
import { StateCreator, StoreMutatorIdentifier } from '../vanilla'
|
||||
import { NamedSet } from './devtools'
|
||||
|
||||
type Write<T extends object, U extends object> = Omit<T, keyof U> & U
|
||||
@ -20,7 +20,7 @@ type StoreRedux<A extends Action> = {
|
||||
type WithRedux<S, A> = Write<Cast<S, object>, StoreRedux<Cast<A, Action>>>
|
||||
|
||||
type Redux = <
|
||||
T extends State,
|
||||
T extends object,
|
||||
A extends Action,
|
||||
Cms extends [StoreMutatorIdentifier, unknown][] = []
|
||||
>(
|
||||
@ -40,12 +40,12 @@ type PopArgument<T extends (...a: never[]) => unknown> = T extends (
|
||||
? (...a: A) => R
|
||||
: never
|
||||
|
||||
type ReduxImpl = <T extends State, A extends Action>(
|
||||
type ReduxImpl = <T extends object, A extends Action>(
|
||||
reducer: (state: T, action: A) => T,
|
||||
initialState: T
|
||||
) => PopArgument<StateCreator<T & ReduxState<A>, [], []>>
|
||||
|
||||
const reduxImpl: ReduxImpl = (reducer, initial) => (set, get, api) => {
|
||||
const reduxImpl: ReduxImpl = (reducer, initial) => (set, _get, api) => {
|
||||
type S = typeof initial
|
||||
type A = Parameters<typeof reducer>[1]
|
||||
;(api as any).dispatch = (action: A) => {
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
import {
|
||||
State,
|
||||
StateCreator,
|
||||
StateListener,
|
||||
StoreMutatorIdentifier,
|
||||
Subscribe,
|
||||
} from '../vanilla'
|
||||
import { StateCreator, StoreMutatorIdentifier } from '../vanilla'
|
||||
|
||||
type SubscribeWithSelector = <
|
||||
T extends State,
|
||||
T extends object,
|
||||
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = []
|
||||
>(
|
||||
@ -22,7 +16,7 @@ type Write<T extends object, U extends object> = Omit<T, keyof U> & U
|
||||
type Cast<T, U> = T extends U ? T : U
|
||||
|
||||
type WithSelectorSubscribe<S> = S extends { getState: () => infer T }
|
||||
? Write<S, StoreSubscribeWithSelector<Cast<T, State>>>
|
||||
? Write<S, StoreSubscribeWithSelector<Cast<T, object>>>
|
||||
: never
|
||||
|
||||
declare module '../vanilla' {
|
||||
@ -32,7 +26,7 @@ declare module '../vanilla' {
|
||||
}
|
||||
}
|
||||
|
||||
type StoreSubscribeWithSelector<T extends State> = {
|
||||
type StoreSubscribeWithSelector<T extends object> = {
|
||||
subscribe: {
|
||||
(listener: (selectedState: T, previousSelectedState: T) => void): () => void
|
||||
<U>(
|
||||
@ -46,7 +40,7 @@ type StoreSubscribeWithSelector<T extends State> = {
|
||||
}
|
||||
}
|
||||
|
||||
type SubscribeWithSelectorImpl = <T extends State>(
|
||||
type SubscribeWithSelectorImpl = <T extends object>(
|
||||
storeInitializer: PopArgument<StateCreator<T, [], []>>
|
||||
) => PopArgument<StateCreator<T, [], []>>
|
||||
|
||||
@ -59,9 +53,10 @@ type PopArgument<T extends (...a: never[]) => unknown> = T extends (
|
||||
const subscribeWithSelectorImpl: SubscribeWithSelectorImpl =
|
||||
(fn) => (set, get, api) => {
|
||||
type S = ReturnType<typeof fn>
|
||||
const origSubscribe = api.subscribe as Subscribe<S>
|
||||
type Listener = (state: S, previousState: S) => void
|
||||
const origSubscribe = api.subscribe as (listener: Listener) => () => void
|
||||
api.subscribe = ((selector: any, optListener: any, options: any) => {
|
||||
let listener: StateListener<S> = selector // if no selector
|
||||
let listener: Listener = selector // if no selector
|
||||
if (optListener) {
|
||||
const equalityFn = options?.equalityFn || Object.is
|
||||
let currentSlice = selector(api.getState())
|
||||
|
||||
37
src/react.ts
37
src/react.ts
@ -5,11 +5,8 @@ import { useDebugValue } from 'react'
|
||||
// The following is a workaround until ESM is supported.
|
||||
import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector'
|
||||
import createStore, {
|
||||
EqualityChecker,
|
||||
Mutate,
|
||||
State,
|
||||
StateCreator,
|
||||
StateSelector,
|
||||
StoreApi,
|
||||
StoreMutatorIdentifier,
|
||||
} from './vanilla'
|
||||
@ -18,24 +15,22 @@ const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports
|
||||
|
||||
type ExtractState<S> = S extends { getState: () => infer T } ? T : never
|
||||
|
||||
type WithReact<S extends StoreApi<State>> = S & {
|
||||
type WithReact<S extends StoreApi> = S & {
|
||||
getServerState?: () => ExtractState<S>
|
||||
}
|
||||
|
||||
export function useStore<S extends WithReact<StoreApi<State>>>(
|
||||
api: S
|
||||
): ExtractState<S>
|
||||
export function useStore<S extends WithReact<StoreApi>>(api: S): ExtractState<S>
|
||||
|
||||
export function useStore<S extends WithReact<StoreApi<State>>, U>(
|
||||
export function useStore<S extends WithReact<StoreApi>, U>(
|
||||
api: S,
|
||||
selector: StateSelector<ExtractState<S>, U>,
|
||||
equalityFn?: EqualityChecker<U>
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equalityFn?: (a: U, b: U) => boolean
|
||||
): U
|
||||
|
||||
export function useStore<TState extends State, StateSlice>(
|
||||
export function useStore<TState extends object, StateSlice>(
|
||||
api: WithReact<StoreApi<TState>>,
|
||||
selector: StateSelector<TState, StateSlice> = api.getState as any,
|
||||
equalityFn?: EqualityChecker<StateSlice>
|
||||
selector: (state: TState) => StateSlice = api.getState as any,
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
||||
) {
|
||||
const slice = useSyncExternalStoreWithSelector(
|
||||
api.subscribe,
|
||||
@ -48,25 +43,25 @@ export function useStore<TState extends State, StateSlice>(
|
||||
return slice
|
||||
}
|
||||
|
||||
export type UseBoundStore<S extends WithReact<StoreApi<State>>> = {
|
||||
export type UseBoundStore<S extends WithReact<StoreApi>> = {
|
||||
(): ExtractState<S>
|
||||
<U>(
|
||||
selector: StateSelector<ExtractState<S>, U>,
|
||||
equals?: EqualityChecker<U>
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equals?: (a: U, b: U) => boolean
|
||||
): U
|
||||
} & S
|
||||
|
||||
type Create = {
|
||||
<T extends State, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
<T extends object, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
): UseBoundStore<Mutate<StoreApi<T>, Mos>>
|
||||
<T extends State>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
<T extends object>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
) => UseBoundStore<Mutate<StoreApi<T>, Mos>>
|
||||
<S extends StoreApi<State>>(store: S): UseBoundStore<S>
|
||||
<S extends StoreApi>(store: S): UseBoundStore<S>
|
||||
}
|
||||
|
||||
const createImpl = <T extends State>(createState: StateCreator<T, [], []>) => {
|
||||
const createImpl = <T extends object>(createState: StateCreator<T, [], []>) => {
|
||||
const api =
|
||||
typeof createState === 'function' ? createStore(createState) : createState
|
||||
|
||||
@ -78,7 +73,7 @@ const createImpl = <T extends State>(createState: StateCreator<T, [], []>) => {
|
||||
return useBoundStore
|
||||
}
|
||||
|
||||
const create = (<T extends State>(
|
||||
const create = (<T extends object>(
|
||||
createState: StateCreator<T, [], []> | undefined
|
||||
) => (createState ? createImpl(createState) : createImpl)) as Create
|
||||
|
||||
|
||||
209
src/vanilla.ts
209
src/vanilla.ts
@ -1,3 +1,114 @@
|
||||
type SetStateInternal<T> = {
|
||||
_(
|
||||
partial: T | Partial<T> | { _(state: T): T | Partial<T> }['_'],
|
||||
replace?: boolean | undefined
|
||||
): void
|
||||
}['_']
|
||||
|
||||
export interface StoreApi<T extends object = object> {
|
||||
setState: SetStateInternal<T>
|
||||
getState: () => T
|
||||
subscribe: (listener: (state: T, prevState: T) => void) => () => void
|
||||
destroy: () => void
|
||||
}
|
||||
|
||||
type Get<T, K, F = never> = K extends keyof T ? T[K] : F
|
||||
|
||||
export type Mutate<S, Ms> = Ms extends []
|
||||
? S
|
||||
: Ms extends [[infer Mi, infer Ma], ...infer Mrs]
|
||||
? Mutate<StoreMutators<S, Ma>[Mi & StoreMutatorIdentifier], Mrs>
|
||||
: never
|
||||
|
||||
export type StateCreator<
|
||||
T extends object,
|
||||
Mis extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mos extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
U = T
|
||||
> = ((
|
||||
setState: Get<Mutate<StoreApi<T>, Mis>, 'setState', undefined>,
|
||||
getState: Get<Mutate<StoreApi<T>, Mis>, 'getState', undefined>,
|
||||
store: Mutate<StoreApi<T>, Mis>,
|
||||
$$storeMutations: Mis
|
||||
) => U) & { $$storeMutators?: Mos }
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface
|
||||
export interface StoreMutators<S, A> {}
|
||||
export type StoreMutatorIdentifier = keyof StoreMutators<unknown, unknown>
|
||||
|
||||
type CreateStore = {
|
||||
<T extends object, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
): Mutate<StoreApi<T>, Mos>
|
||||
|
||||
<T extends object>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
) => Mutate<StoreApi<T>, Mos>
|
||||
}
|
||||
|
||||
type CreateStoreImpl = <
|
||||
T extends object,
|
||||
Mos extends [StoreMutatorIdentifier, unknown][] = []
|
||||
>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
) => Mutate<StoreApi<T>, Mos>
|
||||
|
||||
type PopArgument<T extends (...a: never[]) => unknown> = T extends (
|
||||
...a: [...infer A, infer _]
|
||||
) => infer R
|
||||
? (...a: A) => R
|
||||
: never
|
||||
|
||||
const createStoreImpl: CreateStoreImpl = (createState) => {
|
||||
type TState = ReturnType<typeof createState>
|
||||
type Listener = (state: TState, prevState: TState) => void
|
||||
let state: TState
|
||||
const listeners: Set<Listener> = new Set()
|
||||
|
||||
const setState: SetStateInternal<TState> = (partial, replace) => {
|
||||
// TODO: Remove type assertion once https://github.com/microsoft/TypeScript/issues/37663 is resolved
|
||||
// https://github.com/microsoft/TypeScript/issues/37663#issuecomment-759728342
|
||||
const nextState =
|
||||
typeof partial === 'function'
|
||||
? (partial as (state: TState) => TState)(state)
|
||||
: partial
|
||||
if (nextState !== state) {
|
||||
const previousState = state
|
||||
state = replace
|
||||
? (nextState as TState)
|
||||
: Object.assign({}, state, nextState)
|
||||
listeners.forEach((listener) => listener(state, previousState))
|
||||
}
|
||||
}
|
||||
|
||||
const getState: () => TState = () => state
|
||||
|
||||
const subscribe: (listener: Listener) => () => void = (listener) => {
|
||||
listeners.add(listener)
|
||||
// Unsubscribe
|
||||
return () => listeners.delete(listener)
|
||||
}
|
||||
|
||||
const destroy: () => void = () => listeners.clear()
|
||||
const api = { setState, getState, subscribe, destroy }
|
||||
state = (createState as PopArgument<typeof createState>)(
|
||||
setState,
|
||||
getState,
|
||||
api
|
||||
)
|
||||
return api as any
|
||||
}
|
||||
|
||||
const createStore = ((createState) =>
|
||||
createState ? createStoreImpl(createState) : createStoreImpl) as CreateStore
|
||||
|
||||
export default createStore
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @deprecated Use `object` instead of `State`
|
||||
*/
|
||||
export type State = object
|
||||
|
||||
/**
|
||||
@ -55,101 +166,3 @@ export type GetState<T extends State> = () => T
|
||||
* @deprecated Use `StoreApi<T>['destroy']` instead of `GetState<T>`.
|
||||
*/
|
||||
export type Destroy = () => void
|
||||
|
||||
export interface StoreApi<T extends State> {
|
||||
setState: SetState<T>
|
||||
getState: GetState<T>
|
||||
subscribe: Subscribe<T>
|
||||
destroy: Destroy
|
||||
}
|
||||
|
||||
export type StateCreator<
|
||||
T extends State,
|
||||
Mis extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mos extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
U = T
|
||||
> = ((
|
||||
setState: Get<Mutate<StoreApi<T>, Mis>, 'setState', undefined>,
|
||||
getState: Get<Mutate<StoreApi<T>, Mis>, 'getState', undefined>,
|
||||
store: Mutate<StoreApi<T>, Mis>,
|
||||
$$storeMutations: Mis
|
||||
) => U) & { $$storeMutators?: Mos }
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface
|
||||
export interface StoreMutators<S, A> {}
|
||||
export type StoreMutatorIdentifier = keyof StoreMutators<unknown, unknown>
|
||||
|
||||
export type Mutate<S, Ms> = Ms extends []
|
||||
? S
|
||||
: Ms extends [[infer Mi, infer Ma], ...infer Mrs]
|
||||
? Mutate<StoreMutators<S, Ma>[Mi & StoreMutatorIdentifier], Mrs>
|
||||
: never
|
||||
|
||||
type Get<T, K, F = never> = K extends keyof T ? T[K] : F
|
||||
|
||||
type CreateStore = {
|
||||
<T extends State, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
): Mutate<StoreApi<T>, Mos>
|
||||
|
||||
<T extends State>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
) => Mutate<StoreApi<T>, Mos>
|
||||
}
|
||||
|
||||
type CreateStoreImpl = <
|
||||
T extends State,
|
||||
Mos extends [StoreMutatorIdentifier, unknown][] = []
|
||||
>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
) => Mutate<StoreApi<T>, Mos>
|
||||
|
||||
type PopArgument<T extends (...a: never[]) => unknown> = T extends (
|
||||
...a: [...infer A, infer _]
|
||||
) => infer R
|
||||
? (...a: A) => R
|
||||
: never
|
||||
|
||||
const createStoreImpl: CreateStoreImpl = (createState) => {
|
||||
type TState = ReturnType<typeof createState>
|
||||
let state: TState
|
||||
const listeners: Set<StateListener<TState>> = new Set()
|
||||
|
||||
const setState: SetState<TState> = (partial, replace) => {
|
||||
// TODO: Remove type assertion once https://github.com/microsoft/TypeScript/issues/37663 is resolved
|
||||
// https://github.com/microsoft/TypeScript/issues/37663#issuecomment-759728342
|
||||
const nextState =
|
||||
typeof partial === 'function'
|
||||
? (partial as (state: TState) => TState)(state)
|
||||
: partial
|
||||
if (nextState !== state) {
|
||||
const previousState = state
|
||||
state = replace
|
||||
? (nextState as TState)
|
||||
: Object.assign({}, state, nextState)
|
||||
listeners.forEach((listener) => listener(state, previousState))
|
||||
}
|
||||
}
|
||||
|
||||
const getState: GetState<TState> = () => state
|
||||
|
||||
const subscribe: Subscribe<TState> = (listener: StateListener<TState>) => {
|
||||
listeners.add(listener)
|
||||
// Unsubscribe
|
||||
return () => listeners.delete(listener)
|
||||
}
|
||||
|
||||
const destroy: Destroy = () => listeners.clear()
|
||||
const api = { setState, getState, subscribe, destroy }
|
||||
state = (createState as PopArgument<typeof createState>)(
|
||||
setState,
|
||||
getState,
|
||||
api
|
||||
)
|
||||
return api as any
|
||||
}
|
||||
|
||||
const createStore = ((createState) =>
|
||||
createState ? createStoreImpl(createState) : createStoreImpl) as CreateStore
|
||||
|
||||
export default createStore
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user