简体   繁体   English

从 Typescript 中的联合类型推断回调参数类型的最佳方法是什么?

[英]What's the best way to infer the callback argument type from a union type in Typescript?

I am trying to write a helper that let's me subscribe to a specific type of dispatched actions in a React app.我正在尝试编写一个帮助程序,让我在 React 应用程序中订阅特定类型的调度操作。 These are the action interfaces I'm working with:这些是我正在使用的操作界面:

enum ActionTypes {
  MoveToLine = 'MOVE_TO_LINE',
  MoveToColumn = 'MOVE_TO_COLUMN',
}

interface MoveToLineAction {
  type: ActionTypes.MoveToLine;
  payload: { line: number };
}

interface MoveToColumnAction {
  type: ActionTypes.MoveToColumn;
  payload: { column: number };
}

type Actions = MoveToLineAction | MoveToColumnAction;

And here's the implementation of subscribeToAction :这是subscribeToAction的实现:

const subscribeToAction = <A extends { type: string }, T extends ActionTypes>(
  type: T,
  onAction: (a: A) => void,
) => (action: A) => {
  if (action.type === type) {
    onAction(action);
  }
};

I want to be able to use it like this:我希望能够像这样使用它:

// Subscribe to a specific type of action
subscribeToAction(ActionTypes.MoveToLine, action => {
  action.payload.line;
});

The problem I'm running into is that I want the type of action in the onAction listener to be inferred automatically.我遇到的问题是我希望自动推断onAction侦听器中的action类型。 In the code above, I get a Property 'payload' does not exist on type '{ type: string; }'在上面的代码中,我得到一个Property 'payload' does not exist on type '{ type: string; }' Property 'payload' does not exist on type '{ type: string; }' error. Property 'payload' does not exist on type '{ type: string; }'错误。 I can manually type the listener to get around this like this:我可以手动输入监听器来解决这个问题:

subscribeToAction(ActionTypes.MoveToLine, (action: MoveToLineAction) => {
  action.payload.line;
});

But it seems redundant to pass in an ActionType and then also have to specify the action type in the listener.但是传入一个ActionType然后还必须在侦听器中指定操作类型似乎是多余的。 Do you guys have any suggestions as to how to avoid doing this?你们对如何避免这样做有什么建议吗?

Here's how I'd go about it:以下是我的处理方式:

type ActionOfType<T extends ActionTypes> = Extract<Actions, { type: T }>;

const subscribeToAction = <T extends ActionTypes>(
    type: T,
    onAction: (a: ActionOfType<T>) => void,
) => (action: Actions) => {
    if (action.type === type) {
        // assertion necessary or explicit type guard: 
        onAction(action as ActionOfType<T>);
    }
};

subscribeToAction(ActionTypes.MoveToLine,
    action => { // action inferred as MoveToLineAction
        action.payload.line;
    }
);

You don't really want two generic parameters in subscribeToAction() .您真的不需要subscribeToAction()两个通用参数。 You want T corresponding to the ActionTypes constituent passed in as type , and you want the argument to the onAction callback to automatically be constrained to the corresponding constituent of Actions .您希望T对应于作为type传入的ActionTypes成分,并且您希望onAction回调的参数自动约束到Actions的相应成分。 The ActionOfType<T> type function shows how you can do this. ActionOfType<T>类型函数显示了如何执行此操作。 Oh, and the return value of subscribeToAction should be a function that takes any Actions , right?哦, subscribeToAction的返回值应该是一个接受任何Actions的函数,对吧? That's why you do the if (action.type === type) check.这就是为什么要执行if (action.type === type)检查。

Also note that because action is of type Actions , you need to tell the compiler that the test if (action.type === type) actually narrows action down to ActionOfType<T> .还要注意,因为actionActions类型,所以你需要告诉编译器测试if (action.type === type)实际上将action缩小到ActionOfType<T> I just use a type assertion.我只是使用类型断言。

Okay, hope that helps you.好的,希望对你有帮助。 Good luck!祝你好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM