fix(types): avoid using deprecated types (#1122)

* fix(types): avoid using deprecated types

* simplify a bit
This commit is contained in:
Daishi Kato 2022-07-21 08:15:29 +09:00 committed by GitHub
parent 0938b17a77
commit 49dcec03ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 167 additions and 181 deletions

View File

@ -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>

View File

@ -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][] = []
>(

View File

@ -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
}

View File

@ -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, [], []>>

View File

@ -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, [], []>>

View File

@ -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) => {

View File

@ -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())

View File

@ -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

View File

@ -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