我遵循 Improved Redux type safety with TypeScript 2.8 中显示的模式,但我想添加一个转折点。我的一些操作可在不同的上下文中重用,但需要一些额外的识别信息才能在 reducer 中对它们进行分类。
我想我可以通过添加一些高阶 Action 创建者修饰符来解决这个问题并使我的代码简短。这些将接受现有的 Action 创建者并返回一个新的 Action 创建者,它将一些信息附加到我的 Flux 标准 Action 的 meta
键。
// Removed `payload` and `error` for this reproduction
interface FluxStandardAction<T extends string, Meta> {
type: T;
meta?: Meta;
}
// Our basic actions
enum ActionType {
One = "one",
Two = "two",
}
interface Action1 {
type: ActionType.One;
}
interface Action2 {
type: ActionType.Two;
}
type Action = Action1 | Action2;
function action1(): Action1 {
return { type: ActionType.One }
}
function action2(): Action2 {
return { type: ActionType.Two }
}
// Higher order action modifiers that augment the meta
interface MetaAlpha {
meta: {
alpha: string;
}
}
function addMetaAlpha<T extends string, M extends {}, A extends FluxStandardAction<T, M>>(action: () => A, alpha: string) {
return function (): A & MetaAlpha {
let { type, meta } = action();
return { type, meta }; // Error here
}
}
这会产生错误:
Type '{ type: T; meta: M; }' is not assignable to type 'A & MetaAlpha'.
Object literal may only specify known properties, but 'type' does not exist in type 'A & MetaAlpha'. Did you mean to write 'type'?
虽然我希望能更好地理解此错误消息,但我的问题实际上是关于哪些技术适合构建高阶 Action 创建器。
meta
键是否是实现高阶 Action 创建器的适当方式?如果是这样,我如何以编译器满意的方式实现 addMetaAlpha
?处理这些增强操作的类型安全 reducer 会是什么样子?
最佳答案
该错误有点误导,但原因是您正试图在需要泛型类型的地方分配一个对象文字,A extends FluxStandardAction
,但这可能意味着除了 type
和 meta
,A
可以有其他成员,因此编译器无法真正检查对象字面量是否符合 A 的形状
。以下赋值在您编写的函数中是有效的,因为属性是已知的,因此可以对其进行检查:
let result : FluxStandardAction<T, M> = { type: type, meta };
如果您想返回一个具有所有原始属性和新的 meta
属性的对象,您可以使用 Object.assign
因为它将返回一个交集类型类型参数。
function addMetaAlpha<A extends FluxStandardAction<string, any>>(action: () => A, alpha: string) {
return function (): A & MetaAlpha {
let act = action();
return Object.assign(act, {
meta: {
alpha
}
})
}
}
var metaAct1 = addMetaAlpha(action1, "DD"); // () => Action1 & MetaAlpha
此外,我不会让 meta
成为可选的,因为如果操作被扩充,属性将存在,我会改变函数的约束(尽管你应该看到它如何与你的其余部分交互代码库):
function addMetaAlpha<A extends { type: string }>(action: () => A, alpha: string) {
return function (): A & MetaAlpha {
let act = action();
return Object.assign(act, {
meta: {
alpha
}
})
}
}
var metaAct1 = addMetaAlpha(action1, "DD"); // () => Action1 & MetaAlpha
var metaAct2 : () => FluxStandardAction<'one', { alpha: string }> = metaAct1; // Is is assignable to the interface if we need it to be;
至于这是否是执行 HOC 操作的标准方式,我不能说,但它确实是一种有效的执行方式。
关于typescript - 如何创建类型安全的高阶 Redux Action 创建器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50828662/