TypeScript factory pattern, and types export suggestion/question. #1520
-
We recently upgraded from 3.x and had to add .d.ts file for zustand locally, because some types, like, Our use case / pattern is this: import produce from 'immer';
import create, { GetState, SetState } from 'zustand';
/**
* **TL;DR, use factories and type casting for store type inference.**
*
* Concrete initial state, without a hand-jammed TS interface.
*/
const initialState = {
// required props are usually fine and require no additional work.
assignments: new Map<string, boolean>(),
// optional properties / things that can't be inferred just require a little extra doc / type casting.
optionalProp: undefined as boolean | undefined
};
/**
* State based on a concrete state object. Initial state could also be a factory return if use case needs it.
*/
type IBearState = typeof initialState;
/**
* Store type is the state, plus an actions object, which is just to isolate state from
* things which modify state, a little.
*
* Because the actions factory is a function declaration and will be hoisted,
* we can refer to its return type right away.
*
* **All this type inference works just fine. Successful type inference.**
*/
type IBearStore = IBearState & { actions: ReturnType<typeof createBearActions> };
/**
* Un-curried create works fine for this pattern.
*/
export const useBearStore = create<IBearStore>((set, get) => ({
...initialState,
actions: createBearActions(set, get),
}));
/**
* This factory for things which can update state depends on being passed set and get
* @param set
* @param get
* @returns actions object
*/
export function createBearActions(set: SetState<IBearStore>, get: GetState<IBearStore>) {
return {
setBear(name: string, sleeping = false) {
set(
produce<IBearStore>(state => {
state.assignments.set(name, sleeping)
})
);
},
};
}
//#region typed selectors
export function getBearState(state: IBearStore) {
return state.assignments;
}
//#endregion typed selectors Is there a reason this pattern is bad? I was kind of proud of it, and think it would make a good addition to the TypeScript docs for people who want to be able to infer types, except that some types involved are no longer exported / deprecated. Is there a solid reason not to export some of these? Maybe we missed something really obvious, about StoreApi, e.g. and it should just be: export function createBearActions(set: StoreApi<IBearStore>['setState'], get: StoreApi<IBearStore>['getState']) { But these are the types we ended up adding locally to work around TS export changes // contains all missing typings for zustand library
// these types are not exported by zustand.
export type StoreSubscribeWithSelector<T> = {
subscribe: {
(listener: (selectedState: T, previousSelectedState: T) => void): () => void;
<U>(
selector: (state: T) => U,
listener: (selectedState: U, previousSelectedState: U) => void,
options?: {
equalityFn?: (a: U, b: U) => boolean;
fireImmediately?: boolean;
}
): () => void;
};
};
export type Write<T, U> = Omit<T, keyof U> & U;
export type SetState<T> = (state: T | Partial<T> | ((state: T) => T)) => void;
export type GetState<T> = () => T;
export type EqualityChecker<T> = (a: T, b: T) => boolean;
export type StateSelector<T, U> = (s: T) => U; |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 4 replies
-
@devanshj Can you help explaining the idea behind our decision? |
Beta Was this translation helpful? Give feedback.
-
Welcome to open source, where changes and their discussion happen in open and you can use See description of #772 (which itself links to #715) as to why we've removed those types. The PR and hence v3.7.0 also came with Perhaps you overlooked but opposed to what you suggest, the changes are very much mentioned in the release notes, see the release notes of v3.7.0. And see the release notes of v4.0.0-rc.0 for further migration. As for the pattern I don't see any reason why it should be "bad", seems okay to me. If all you want to do is infer the state type then we already have
|
Beta Was this translation helpful? Give feedback.
Welcome to open source, where changes and their discussion happen in open and you can use
git blame
to see them ;) (applies to you too @dai-shi :P)See description of #772 (which itself links to #715) as to why we've removed those types. The PR and hence v3.7.0 also came with
@deprecated
jsdocs on how to migrate. The current deprecations are in #1089 but the reasoning remains the same, and they too have the migration in jsdocs.Perhaps you overlooked but opposed to what you suggest, the changes are very much mentioned in the release notes, see the release notes of v3.7.0. And see the release notes of v4.0.0-rc.0 for further migration.
As for the pattern I don't see any reason why it shoul…