如何从 Slice(redux 工具包)中获取映射到字符串的类型安全的 POJO 操作
How can I get a typesafe POJO of actions mapped to a string from a Slice (redux toolkit)
我正在尝试设置一个函数,该函数 returns 将 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
}
感谢您花时间阅读本文,我特别感谢所有花时间尝试解决此问题的人。我知道你的时间很宝贵:)
查看您的 codesandbox 代码,keyof typeof slice.actions
似乎不够通用,无法表达您要执行的操作。即使 slice
是扩展 Slice
的泛型类型 T
,当您评估 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()
现在returnsRecord<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'?
我觉得不错。好的,希望有所帮助;祝你好运!
我正在尝试设置一个函数,该函数 returns 将 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
}
感谢您花时间阅读本文,我特别感谢所有花时间尝试解决此问题的人。我知道你的时间很宝贵:)
查看您的 codesandbox 代码,keyof typeof slice.actions
似乎不够通用,无法表达您要执行的操作。即使 slice
是扩展 Slice
的泛型类型 T
,当您评估 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()
现在returnsRecord<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'?
我觉得不错。好的,希望有所帮助;祝你好运!