如何从切片(redux 工具包)中获取映射到字符串的操作的类型安全 POJO
Posted
技术标签:
【中文标题】如何从切片(redux 工具包)中获取映射到字符串的操作的类型安全 POJO【英文标题】:How can I get a typesafe POJO of actions mapped to a string from a Slice (redux toolkit) 【发布时间】:2020-09-02 16:42:07 【问题描述】:我正在尝试设置一个函数,该函数返回一个 Slice 的操作名称的类型安全 POJO,映射到相应的操作类型名称(使用 sliceName/sliceAction 的约定)。该功能非常简单,但让 TypeScript 识别按键让我很尴尬。
我正在关注Redux Toolkit's tutorial 中的示例,但我想看看这是否可能。理想情况下,我将能够将该函数与 Redux-Saga 或 Redux-Observable 结合使用,以避免使用字符串文字作为操作类型。
我尝试设置了一个codesandbox。
const getActions = <T extends Slice>(slice: T) =>
const keys = Object.keys(slice.actions) as Array<keyof typeof slice.actions>;
return keys.reduce(
(accumulator, key) =>
accumulator[key] = `$slice.name/$key`;
return accumulator;
,
as Record<keyof typeof slice.actions, string>
);
;
目的是能够像这样截取一个片段:
const issuesDisplaySlice = createSlice(
name: "issuesDisplay",
initialState,
reducers:
displayRepo(state, action: PayloadAction<CurrentRepo>)
//...
,
setCurrentPage(state, action: PayloadAction<number>)
//...
);
并获得一个 TypeScript 来识别输出:
displayRepo: string,
setCurrentPage: string
感谢您花时间阅读本文,我特别感谢任何花时间尝试解决问题的人。我知道你的时间很宝贵:)
【问题讨论】:
【参考方案1】:查看您的代码和框代码,keyof typeof slice.actions
似乎不足以表达您想要做的事情。尽管slice
是泛型类型T
扩展Slice
,但当您评估slice.actions
时,编译器会将其视为Slice
:
const actions = slice.actions; // CaseReducerActions<SliceCaseReducers<any>>
这很不幸,因为keyof CaseReducerActions<SliceCaseReducers<any>>
(又名keyof Slice['actions']
)只是string | number
,而不是您在T
上传递的特定键:
type KeyofSliceActions = keyof Slice['actions'];
// type KeyofSliceActions = string | number
将泛型值扩大到它们对属性查找的约束可能对性能有好处,但会导致一些像这样的不良情况。有关相关问题,请参阅microsoft/TypeScript#33181。
理想情况下,当您在 T
类型的值上查找 actions
属性时,编译器会将结果视为 lookup type T['actions']
。这不会自动发生,但您可以要求编译器使用类型注释来执行此操作:
const genericActions: T['actions'] = slice.actions; // this is acceptable
现在,如果您将其余代码中的 slice.actions
实例替换为 genericActions
,则类型将按照您希望的方式工作:
const getActions = <T extends Slice>(slice: T) =>
const genericActions: T['actions'] = slice.actions; // this is acceptable
const keys = Object.keys(genericActions) as Array<keyof typeof genericActions>;
return keys.reduce(
(accumulator, key) =>
accumulator[key] = `$slice.name/$key`;
return accumulator;
,
as Record<keyof typeof genericActions, string>
);
;
// const getActions: <T extends Slice<any, SliceCaseReducers<any>, string>>(
// slice: T) => Record<keyof T["actions"], string>
(旁白:keyof typeof genericActions
可以缩短为keyof T['actions']
。)您可以看到getActions()
现在返回Record<keyof T["actions"], string>
,这是一个依赖于T
的类型。
让我们看看它是否有效:
const actions = getActions<typeof issuesDisplaySlice>(issuesDisplaySlice);
// const actions: Record<"displayRepo" | "setCurrentPage" | "setCurrentDisplayType", string>
actions.displayRepo.toUpperCase(); // okay
actions.displayTypo.toUpperCase(); // error!
// ---> ~~~~~~~~~~~
// Property 'displayTypo' does not exist on type
// 'Record<"displayRepo" | "setCurrentPage" | "setCurrentDisplayType", string>'.
// Did you mean 'displayRepo'?
我觉得不错。好的,希望有帮助;祝你好运!
Playground link to code
【讨论】:
我忘了评论这个。太棒了,非常感谢!以上是关于如何从切片(redux 工具包)中获取映射到字符串的操作的类型安全 POJO的主要内容,如果未能解决你的问题,请参考以下文章
如何从 React 组件 Redux Toolkit 中的 createAsyncThunk 获取结果
如何正确使用 redux 工具包的 preloadedState 和 createSlice?