Я использую Redux и разбил данные магазина на «кусочки».Каждый «срез» соответствует области, такой как users
или comments
.Я объединяю селекторы для каждого из этих срезов в один модуль selectors
верхнего уровня, к которому я получаю доступ во всем приложении.
Срез имеет формат:
export interface IUsersSelectors {
getCurrentUser(state: IUsersState): IUser | undefined;
}
const selectors: IUsersSelectors = {
getCurrentUser(state: IUsersState) {
return state.currentUser;
}
};
export default {
getInitialState,
reducer,
selectors
};
, которые затемвсе импортировано, и селекторы объединены:
export const selectors = Object.keys(slices).reduce((combinedSelectors: any, sliceKey: string) => {
const sliceSelectors = slices[sliceKey].selectors;
combinedSelectors[sliceKey] = Object.keys(sliceSelectors).reduce((selectorsMap: object, selectorKey: string) => {
const localizedSelector = sliceSelectors[selectorKey];
selectorsMap[selectorKey] = (globalState, ...args: any[]): any => {
return localizedSelector(globalState[sliceKey], ...args);
};
return selectorsMap;
}, {});
return combinedSelectors;
}, {});
А затем используются во всем приложении:
selectors.users.getCurrentUser(store.getState());
Это означает, что селекторы ожидают только их состояние среза при извлеченииданные, но на самом деле они вызвали с состоянием глобального хранилища.По сути, я просто оборачиваю их в другую функцию, которая управляет областью видимости.
Самое близкое к этому определение общих типов для этого:
type IScopedSelector<T extends () => any> = (globalState: IStoreState, ...args: any[]) => ReturnType<T>;
type IScopedSelectors<T> = {
[K in keyof T]: IScopedSelector<T[K]>;
};
type INestedScopedSelectors<R> = {
[S in keyof R]: IScopedSelectors<R[S]>;
};
export const selectors: INestedScopedSelectors<ISelectors>...
, где ISelectors
- этопростой интерфейс формы:
export interface ISelectors {
users: IUsersSelectors;
}
Тем не менее, я получаю сообщение об ошибке при наборе T[K]
в IScopedSelector
, так как это должна быть функция:
[ts] Type 'T[K]' does not satisfy the constraint '() => any'.
Если я удаляю extends () => any
, то получаю ошибку о ReturnType
:
[ts] Type 'T' does not satisfy the constraint '(...args: any[]) => any'.
В идеале, я бы также поддержал типизацию параметров селектора (а не ...args: any[]
), переопределяя только первый аргумент, чтобы быть состоянием глобального хранилища.
Есть ли лучший способ обработки вложенных обобщенных типов, подобных этому?Это вообще возможно?