typescript - 如何创建类型安全的高阶 Redux Action 创建器?

标签 typescript redux

我遵循 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,但这可能意味着除了 typemetaA 可以有其他成员,因此编译器无法真正检查对象字面量是否符合 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/

相关文章:

angular - Angular ListView 组件,它将接受任何数据源

reactjs - 在 Redux 连接组件中保留类型参数

typescript - typescript 中特定类型的数组

html - Angular 6 - 从 httpClient 获取 csv 响应

javascript - Angular2 ngIf 在 false 时创建空元素

javascript - 构建本地化 react/redux 应用程序的存储

javascript - 传递一个空的 Prop (没有 api 值)来 react 组件

javascript - 在函数内部使用公共(public)变量

javascript - React Redux 使用连接组件的 HOC

javascript - 亚马逊土耳其机器人 : How to use react/redux to implement HIT?