繁体   English   中英

打字稿泛型函数重载

[英]Typescript generics function overloading

我试图从最具体的开始添加函数参数重载,但类型缩小似乎不起作用。 还尝试将参数更改为联合类型,但类型保护也不起作用。 我错过了什么?

type IReducer<S, A> = (state: S, action: A) => S;

interface IAsyncHandlers<S, A extends IAction> {
  request?: IReducer<S, A>;
  success?: IReducer<S, A>;
  failure?: IReducer<S, A & { payload: any; error: true }>;
}

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

const getActionHandler = <S, A>(handler?: IReducer<S, A>) => (state: S) => (action: A): S =>
  handler ? handler(state, action) : state;

const handleAsyncAction = <S, A extends IAction>(handlers: IAsyncHandlers<S, A>): IReducer<S, A> => {
  function reducer(state: S, action: A): S 
  function reducer(state: S, action: A & { error: true }): S;
  function reducer(state: S, action: A & { meta: { isPending: true } }): S;

  function reducer(state: S, action: A & { error?: any; meta?: any } ): S {  
    switch (true) {
      case action.error:
        // Property 'error' is optional in type 'IAction & { error?: any; meta?: any; }' 
        // but required in type '{ payload: any; error: true; }'.
        return getActionHandler(handlers.failure)(state)(action);

      case action.meta && action.meta.isPending:
        return getActionHandler(handlers.request)(state)(action);

      default:
        return getActionHandler(handlers.success)(state)(action);
    }
  }

  return reducer;
};

这里发生了很多事情。

首先,你真的不需要重载:这些签名的唯一区别是action的类型。 当多个签名以某种协调方式不同时,重载很有用; 例如,如果函数返回值的类型取决于action的类型,或者如果state参数类型取决于action的类型。 由于签名中没有任何其他内容取决于action的类型,因此您可以通过(如您尝试)将action更改为联合类型(本质上只是A ,因为A | (A & B) | (A & C) | (A & D)本质上等同A 。)

顺便说一句,您的重载实际上是从最少到最具体的,这与您通常想要的相反。 按从上到下的顺序检查呼叫签名。 如果调用不匹配第一个签名reducer(state: S, action: A): S ,它肯定不会匹配任何后续签名reducer(state: S, action: A & XYZ): S 这意味着只有第一个签名才会在实践中使用。 如果这里需要重载,我会告诉您将更具体的放在首位,并提供有关什么使某些东西“具体”的更多详细信息。 但这并不重要,因为您不需要重载。

您的问题实际上出在函数的实现中,您尝试使用 switch 语句作为action变量类型的类型保护 不幸的是, action类型涉及A ,一个泛型类型参数,并且TypeScript 不会对泛型参数进行缩小 它已被请求 ( microsoft/TypeScript#24085 ),但显然这种缩小会导致编译器出现严重的性能问题。 我对你的建议是使用用户定义的类型保护来对发生的缩小施加更多的控制。 它有点冗长,但它应该可以工作:

const isErrorAction =
  <A extends IAction & { error?: any }>(a: A): a is A & { error: true } =>
    (a.error)

const isRequestAction =
  <A extends IAction & { meta?: { isPending?: any } }>(
    a: A
  ): a is A & { meta: { isPending: true } } =>
    (a.meta && a.meta.isPending);

const handleAsyncAction = <S, A extends IAction>(
  handlers: IAsyncHandlers<S, A>
): IReducer<S, A> => {
  function reducer(state: S, action: A): S {

    if (isErrorAction(action)) {
      return getActionHandler(handlers.failure)(state)(action);
    }

    // not strictly necessary to narrow here, but why not
    if (isRequestAction(action)) {
      return getActionHandler(handlers.request)(state)(action);
    }

    return getActionHandler(handlers.success)(state)(action);
  }

  return reducer;
};

现在实现类型检查没有错误,并且调用签名已简化为一个。

暂无
暂无

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

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