rootState 类型推断错误 | TS2339

Error in infer of rootState type | TS2339

我正在尝试推断 RootState 的类型,但在尝试在选择器中使用它时出现此错误:

const tagsSelector = (state: RootState) => state.tags;
TS2339: Property 'tags' does not exist on type 'CombinedState<{ tags: CombinedState<{ tagsSet: TagsState; }>; } | { queue: CombinedState<{ clientCard: ClientCardState; clientCardTasks: ClientCardTasksState; }>; }>'.   Property 'tags' does not exist on type '{ readonly [$CombinedState]?: undefined; } & { queue: CombinedState<{ clientCard: ClientCardState; clientCardTasks: ClientCardTasksState; }>; }'.

我推断的RootState类型

const typedReducers = typedModules.map(m => m.getTypedReducers() ).reduce((accum, r) => {
    return {...accum, ...r}
});

const rootTypedReducers = combineReducers(typedReducers);

export type RootState = ReturnType<typeof rootTypedReducers>;

getTypedReducers() 只是 return 每个模块的根 reducer

getTypedReducers() {
        return {tags: combineReducers({
                    tagsSet: tagsReducers,
            })};
    }

但是,如果我只使用一个模块,那么一切正常。

打字稿是这样解释你的状态类型的:

CombinedState<{ tags: CombinedState<{ tagsSet: TagsState; }>; } | { queue: CombinedState<{ clientCard: ClientCardState; clientCardTasks: ClientCardTasksState; }>; }>

问题是它是 OR 而不是 AND。您的状态被视为 具有键 'tags''queue' | 'clientCard' | 'clientCardTasks'。因此,就打字稿而言,tags 不能保证始终存在。

这个错误的类型是在您的 typedReducures 中创建的,因为您正在使用像 mapreduce 这样的数组函数。 Typescript 无法理解 return {...accum, ...r} 必须包含单个 getTypedReducers() 映射的所有元素。所以我们需要在那里覆盖 return 在那里输入。

@jcalz 创造了一个经典 ,我们可以在这里使用它。

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

type Reducers = UnionToIntersection<ReturnType<typeof typedModules[number]['getTypedReducers']>>

const typedReducers = typedModules.map(m => m.getTypedReducers()).reduce((accum, r) => {
  return { ...accum, ...r }
}) as Reducers;

现在选择器中不再有错误,因为已知所有键确实存在。

Typescript Playground Link