打字稿重复声明合并

时间:2017-09-28 15:51:53

标签: typescript redux

我目前正致力于使用TypeScript创建类似redux的库。基本动作如下所示:

interface ActionBase {
  type: string;
  payload: any;
}

然后我为每个动作类型扩展了动作界面。例如,对于按钮单击事件,我会有类似的内容:

interface ButtonClickAction extends ActionBase {
  type: 'BUTTON_CLICK';
  payload: {
    // Include some kind of metadata in here
  };
}

然后我添加了一些辅助函数

function isInstanceOfButtonClick(action: ActionBase ): action is ButtonClickAction {
  return action.type === 'BUTTON_CLICK';
}

function buildButtonClickAction(payload): ButtonClickAction {
  return {
    type: 'BUTTON_CLICK',
    payload,
  };
}

问题是我正在为20多种不同类型的行动做这件事。有没有干这样做的方法?对于我需要的每一个动作:

  1. 类型("BUTTON_CLICK"
  2. 的字符串值
  3. 有效载荷的类型
  4. 操作的类型(ButtonClickAction
  5. 构建器功能(buildButtonClickAction
  6. isInstance函数(isInstanceOfButtonClick
  7. 我可以使用类或函数来实现所有具体项目(1,4,5),但我没有干涸的方法来做2和3.现在,我对每个动作都有类似的东西:

    const KEY = 'BUTTON_CLICK';
    namespace ButtonClick {
      export type Payload = {...}
      export interface Action extends ActionBase {
        type: typeof KEY;
        payload: Payload;
      }
    }
    
    let ButtonClick = makeActionValues<typeof KEY, ButtonClick.Payload, ButtonClick.Action>(KEY)
    
    export default ButtonClick;
    

    有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

类似于创建Action工厂字典的函数,每个工厂都有适用于isInstance()类型的buildAction()Action方法?像这样:

interface ActionFactory<T extends string, P> {
  isInstance(action: Action): action is Action<T, P>;
  buildAction(payload: P): Action<T, P>;
}
interface Action<T extends string=string, P=any> {
  type: T,
  payload: P,
}

function getActionFactories<M extends object>(mappings: M): {[T in keyof M]: ActionFactory<T, M[T]>} {
  const ret: any = {};
  Object.keys(mappings).forEach((k: keyof M) => {
    type T = keyof M;
    type P = M[T];
    ret[k] = class Act {
      static isInstance(action: Action): action is Action<T, P> {
        return action.type === k;
      }
      static buildAction(payload: P): Action<T, P> {
        return new Act(payload);        
      }
      type: T = k;
      private constructor(public payload: P) { }
    }
  });
  return ret;
}

您可以通过创建操作键到有效负载类型的映射来使用它:

const _: any = void 0;
const ActionPayloads = {
  ButtonClick: _ as { whatever: string },
  SomeOtherAction: _ as { parameter: number },
  WhoKnows: _ as { notMe: boolean },
}

上面有点难看但是我可能是最干的......否则我需要指定两次关键名称;一次用于有效负载映射,一次用于密钥列表。然后你拨打getActionFactories()

const Actions = getActionFactories(ActionPayloads);

生成的Actions对象有点像命名空间。观察:

const buttonClick = Actions.ButtonClick.buildAction({ whatever: 'hello' });
const someOtherAction = Actions.SomeOtherAction.buildAction({ parameter: 4 });
const whoKnows = Actions.WhoKnows.buildAction({ notMe: false });
const randomAction = Math.random() < 0.33 ? buttonClick : Math.random() < 0.5 ? someOtherAction : whoKnows

if (Actions.WhoKnows.isInstance(randomAction)) {
  console.log(randomAction.payload.notMe);
}

这对你有用吗?

更新1

@darpa said

  

我希望能够获得结果action.payload

的类型

好吧,要获取ButtonClick的有效负载类型,您可以使用ActionPayloads对象并执行以下操作:

const buttonClickPayload: typeof ActionPayloads.ButtonClick = {whatever: 'hello'};
const buttonClick = Actions.ButtonClick.buildAction(buttonClickPayload);

或者,如果您希望Actions公开此类型,则可以向Payload添加幻像ActionFactory属性:

interface ActionFactory<T extends string, P> {
  isInstance(action: Action): action is Action<T, P>;
  buildAction(payload: P): Action<T, P>;
  Payload: P; // phantom property
}

然后你可以像这样引用有效载荷类型:

const buttonClickPayload: typeof Actions.ButtonClick.Payload = {whatever: 'hello'};
const buttonClick = Actions.ButtonClick.buildAction(buttonClickPayload);

但请注意不要实际尝试使用Actions.ButtonClick.Payload,因为它不会真正存在:

console.log(Actions.ButtonClick.Payload.whatever); // okay in TS but blows up at runtime.

希望有所帮助!