Javascript 是否在TypeScript中包装函数的嵌套对象?

Javascript 是否在TypeScript中包装函数的嵌套对象?,javascript,typescript,redux,Javascript,Typescript,Redux,我正在使用Redux,并已将存储数据分解为“切片”。每个“切片”对应一个区域,例如用户或注释。我将每个切片的选择器组合成一个顶级的选择器模块,我可以在整个应用程序中访问该模块 切片的格式如下: export interface IUsersSelectors { getCurrentUser(state: IUsersState): IUser | undefined; } const selectors: IUsersSelectors = { getCurrentUser(stat

我正在使用Redux,并已将存储数据分解为“切片”。每个“切片”对应一个区域,例如
用户
注释
。我将每个切片的选择器组合成一个顶级的
选择器
模块,我可以在整个应用程序中访问该模块

切片的格式如下:

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>...
但是,在尝试将
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[]
),只覆盖第一个参数作为全局存储状态


有没有更好的方法来处理这样的嵌套泛型?这可能吗?

您需要为
IScopedSelectors
INestedScopedSelectors
的泛型类型添加类似的类型约束

如果没有这样的约束,您已经告诉TypeScript,
IScopedSelectors
中的
T
可以是任何类型。因此,
IScopedSelectors
应该是有效的。但是TypeScript正确地指出,对于许多类型(例如
string
),
[K in keyof T]:IScopedSelector
不起作用,因为无法保证
T[K]
遵守
IScopedSelector
施加的约束

因此,解决方案只是向两个接口添加一个约束,这样TypeScript就有了这个保证。为此,内置的
记录
类型可能会有所帮助。比如:

type IScopedSelectors<T extends Record<string, () => any>> = {
  [K in keyof T]: IScopedSelector<T[K]>; // T[K] is now guaranteed to adhere to () => any
};

type INestedScopedSelectors<R extends Record<string, Record<string, () => any>>> = {
  [S in keyof R]: IScopedSelectors<R[S]>; // Similarly, R[S] is now guaranteed to adhere to Record<string, () => any>, exactly what IScopedSelectors is expecting.
};
类型IScopedSelectors>={
[K in keyof T]:IScopedSelector;//T[K]现在保证遵守()=>any
};
输入INestedScopedSelectors>={
[S in keyof R]:IScopedSelectors;//类似地,R[S]现在保证遵守Record any>,这正是IScopedSelectors所期望的。
};
根据具体的用例,您可能希望用更具体的内容替换
记录
类型,但解决方案基本相同。只需确保将约束一直向前推进到层次结构


希望有帮助

谢谢你的回复!我认为这在理论上是有道理的。但是,当我尝试将
ISelectors
接口传递给
InestdScopedSelectors
时,会出现一个错误:
类型“ISelectors”不满足约束“Record>”。类型“ISelectors”中缺少索引签名。
我想我需要
ISelectors
中某个位置的
记录
类型,但不确定该类型是什么样子,因为该接口只是一个简单的映射?您可以通过使用内联映射类型来消除对索引签名的需要。所以
T分别扩展了{[K in keyof T]:()=>any}
R分别扩展了{[S in keyof R]:{[K in keyof R[S]:()=>any}
。但是typescript指出了另一个问题,即:属性“getCurrentUser”的类型不兼容,类型
(state:IUsersState)=>IUser |未定义的
不可分配给
()=>任何
。您可以通过使用
(…args:any[])=>any
而不是
()=>any
来解决这个问题,但是我不确定对于您的用例来说,这种类型是否太允许。真见效了!在
globalState
之后,我确实丢失了所有参数的键入,但是除非您有任何解决方法,否则这仍然比根本不键入要好得多。谢谢
type IScopedSelectors<T extends Record<string, () => any>> = {
  [K in keyof T]: IScopedSelector<T[K]>; // T[K] is now guaranteed to adhere to () => any
};

type INestedScopedSelectors<R extends Record<string, Record<string, () => any>>> = {
  [S in keyof R]: IScopedSelectors<R[S]>; // Similarly, R[S] is now guaranteed to adhere to Record<string, () => any>, exactly what IScopedSelectors is expecting.
};