从 HOC 返回的组件的 Typescript 接口中移除 HOC 注入的 prop?

Remove prop injected by HOC from the Typescript interface of the component returned by HOC?

我正在尝试制作一个高阶组件,它从当前上下文中获取一个函数并将其注入到包装组件中的一个 prop 中,并且仍然保持 Props 接口。

我这样包装:

interface Props extends AsyncRequestHandlerProps {
  bla: string;
}

class MyComponent extends React.Component<Props> {
 // ....
}

export default withAsyncRequestHandler(MyComponent)

我已经这样定义了 withAsyncRequestHandler

export interface AsyncRequestHandlerProps {
  asyncRequestHandler: <T>(promise: Promise<T>) => Promise<T | null>;
}

type PropsWithoutInjectedHandler<P> = Omit<P, keyof AsyncRequestHandlerProps>;


export function withAsyncRequestHandler<P>(Component: React.ComponentType<P>) {
  return class ComponentWithAsyncRequestHandler extends React.Component<
    PropsWithoutInjectedHandler<P>
  > {
    static contextType = AsyncHandlerContext;
    context!: AsyncHandlerContext | null;
    render = () => {
      const asyncRequestHandler: <T>(
        promise: Promise<T>
      ) => Promise<T | null> = (promise) => {
        if (this.context === null) {
          throw new Error(
            "withAsyncRequestHandler should only wrap components that are mounted inside <AsyncHandler />."
          );
        }
        return AsyncRequest(promise, this.context);
      };
      const { ...props } = this.props;
      return (
        <Component
          {...props}
          asyncRequestHandler={asyncRequestHandler}
        ></Component>
      );
    };
  };
}

MyComponent 的直接签名与 bla 道具和 asyncRequestHandler 道具都有。我想要的是包装器 HOC 将 return 一个只有 bla 属性的组件签名,因为 asyncRequestHandler 已经被注入。

这个 HOC 的外部接口似乎可以工作,我仍然可以在安装包装组件时从打字稿中获取剩余的道具。

但是在 HOC 内部我得到一个错误:

我当前的代码在我将 <Component> 挂载到 render() 的那一行给出了这个错误。

Type 'Readonly<Pick<P, Exclude<keyof P, "asyncRequestHandler">>> & { asyncRequestHandler: <T>(promise: Promise<T>) => Promise<T | null>; children?: ReactNode; }' is not assignable to type 'IntrinsicAttributes & P & { children?: ReactNode; }'.
  Type 'Readonly<Pick<P, Exclude<keyof P, "asyncRequestHandler">>> & { asyncRequestHandler: <T>(promise: Promise<T>) => Promise<T | null>; children?: ReactNode; }' is not assignable to type 'P'.
    'P' could be instantiated with an arbitrary type which could be unrelated to 'Readonly<Pick<P, Exclude<keyof P, "asyncRequestHandler">>> & { asyncRequestHandler: <T>(promise: Promise<T>) => Promise<T | null>; children?: ReactNode; }'.ts(2322)

我想问题出在 Omit<P, keyof AsyncRequestHandlerProps> 构造及其用法上?

根据 https://github.com/Microsoft/TypeScript/issues/28938#issuecomment-450636046 这是 TS 中的错误。

Starting with 3.2 the behaviour of the spread operator for generics has changed. Apparently the type of props gets erased as a negative side effect, but you can work around that by casting it back to P using {...props as P} when spreading back into the wrapped component.

所以按照建议,试试这个:

<Component
      {...props as P}
      asyncRequestHandler={asyncRequestHandler}
/>