我怎样才能 refactor/abstract ngrx 操作不那么容易出现拼写错误?

How can I refactor/abstract ngrx actions to be less prone to typos?

更新: 似乎 typescript-fsa 可能是我正在寻找的自以为是的库 https://github.com/aikoven/typescript-fsa/issues/40。任何关于实施细节的帮助都会很棒!

我觉得当我做 ngrx 时,我一直在复制和粘贴文件,然后只是重命名部分。它很容易出现我典型的胖手指错误。虽然我目前没有测试我的项目,但如果一个项目需要 100% 的代码覆盖率,这可能是一项更加乏味的任务。

称为计数器的简单操作示例:

import { Action } from '@ngrx/store';

export const INCREMENT  = '[Counter] Increment';
export const DECREMENT  = '[Counter] Decrement';
export const RESET      = '[Counter] Reset';

export class Increment implements Action {
  readonly type = INCREMENT;
}

export class Decrement implements Action {
  readonly type = DECREMENT;
}

export class Reset implements Action {
  readonly type = RESET;

  constructor(public payload: number) {}
}

export type All
  = Increment
  | Decrement
  | Reset;

问题:

在打字稿中是否有任何模式可以通过简单的函数调用来获得上面显示的这些 类 集的功能?

例如:

TypeAction.create('Animal', ['Add', 'Remove', 'Reset'])

注意:这是一个人为设计的函数签名,因为它没有考虑有效载荷及其类型,而只是举例?

但是这个函数可以generate/represent/work像下面的代码:

import { Action } from '@ngrx/store';

export const ADD = '[Animal] Add';
export const REMOVE = '[Animal] Remove';
export const RESET      = '[Animal] Reset';

export class Add implements Action {
  readonly type = ADD;
}

export class Remove implements Action {
  readonly type = REMOVE;
}

export class Reset implements Action {
  readonly type = RESET;

  constructor() {}
}

export type All
  = Add
  | Remove
  | Reset

最后,请不要反思,这不是一个好的解决方案。此外,"this isn't possible" 也可能是一个可接受的答案...

建议脚手架稍微升级

import { Action } from '@ngrx/store';
import { Animal } from 'animal.models';

export const ADD = '[Auth] Add';

export class Add implements Action {
  readonly type = ADD;
  constructor(public payload: Animal[]) {}
}

我第一次想通过 crud 异步操作来实现更好的 NGRX/redux/flux 方式是害怕的,我使用 apollo 团队对他们所做的带有同步选项的内部异步存储,我认为这是未来,它很好地管理带有一个 true 来源的选项,它们为 CRUD* 操作创建通用操作机制,当你想做一些特别的事情时,你可以使用 DI 并为具有不可变函数的存储编写 reducer,或者我认为你可以使用效果(我还没试过)

为什么我认为是未来? apollo 2.0 不需要你重复 CRUD 操作的代码,每次写入效果以捕获 plus 模式中的异步服务器操作都会通过对 ts 的内省来保护你的类型(生成器已准备好生产),我非常乐观 https://dev-blog.apollodata.com/whats-coming-in-apollo-client-2-0-bcd8ea64acbd(由 James Baxley III 撰写)和 "Future features" 段落 - 当这个未来成为现在正在写的变化时,在服务器、浏览器、p2p live 之间存储和同步状态,将是惊人的...

另一种解决方案是使用像 typescript-fsa*

这样的动作生成器

因此,您可以定义操作 generators,而不是 "native" NgRx 操作 类,例如

import { actionCreatorFactory } from 'typescript-fsa';

const counter = actionCreatorFactory('counter');
export const counterIncrement = counter('INCREMENT');
export const counterDecrement = counter('DECREMENT');
export const counterReset = counter<number>('RESET');

生成的动作将被命名为'counter/INCREMENT'等

像这样在你的减速器中使用它们:

import { isType } from 'typescript-fsa';

export function counterReducer(state: CounterState = initialState, action: Action) {
  if (isType(action, counterIncrement)) {
    return { ...state, < your reduction here > };
  }
  if (isType(action, counterDecrement)) {
    return { ...state, < your reduction here > };
  }
  if (isType(action, counterReset)) {
    return { ...state, < your reduction here > };
  }
  return state;
};

并将这些发送为

store.dispatch(counterIncrement());
store.dispatch(counterDecrement());
store.dispatch(counterReset(42));

最后,在你的效果中

@Effect() counterIncrement$ = this.actions$
  .filter(counterIncrement.match)
  .map(action => ...)

@Effect() counterDecrement$ = this.actions$
  .filter(counterDecrement.match)
  .map(action => ...)

@Effect() counterReset$ = this.actions$
  .filter(counterReset.match)
  .map(action => action.payload)
  .map(payload => ...)

如您所见,除了初始定义之外,您从不使用容易出现错字的操作字符串,而是导入类型化操作生成器,这提供了额外的安全性,并且还可以在支持类型脚本的代码编辑器中启用函数替换。

如果您的操作在各个集合中是同类,即每个集合具有相同 "ADD"、"REMOVE", "RESET" 等操作(但不同的 "collection" 前缀;并不是每个集合都必须实现所有这些),然后你可以更进一步创建 generic 动作生成器,一个 通用 reducer 生成器等,因此您不必多次复制其他相同的代码。

编辑:根据 Matthew 的要求,here 是一个更高级的示例,它结合了泛型和 NgRX 实体**。

*不是该包的作者

**我是那个例子的作者。在这里粘贴完整的示例有点笨拙(并且超出了这个问题的重点),但如果共享 link 不合适,请告诉我。