我正在尝试设置一个函数,该函数返回映射到相应动作类型名称(使用sliceName / sliceAction的约定)的Slice动作名称的类型安全POJO。该功能非常简单,但是让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
}
感谢您抽出宝贵的时间阅读本文档,特别感谢您抽出宝贵时间尝试解决该问题。我知道您的时间很宝贵:)
答案 0 :(得分: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。
理想情况下,当您在类型为actions
的值上查询T
属性时,编译器会将结果视为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'?
对我很好。好吧,希望能有所帮助;祝你好运!