Typescript/React 具有更具体 HTMLElement 引用的 ForwardRefExoticComponent 分配失败

Typescript/React ForwardRefExoticComponent assignment with more specific HTMLElement ref fails

我正在尝试编写一个 HOC 并键入您可以传递给它的子项。

场景看起来有点像这样(也在这个沙盒中:https://codesandbox.io/s/amazing-taussig-h2dpm?file=/src/App.tsx):

interface HOCProps {
    modules: React.ForwardRefExoticComponent<
        HOCChildProps & React.RefAttributes<HTMLElement>
    >[];
}
interface HOCChildProps {
    ...
}
declare const HOC: React.FC<HOCProps>;

// this produces an error in HOCTest if HTMLDivElement is used
// and an error in HOCChildTest if HTMLElement is used
// as the ref type in React.forwardRef
const HOCChildTest = React.forwardRef<HTMLDivElement, HOCChildProps>(
    (props, ref) => (
        <div ref={ref}>
            ...
        </div>
    )
);
const HOCTest: React.FC = () => <HOC modules={[HOCChildTest]} />;

代码仍然有效,但输入无效。

看来这次作业失败了:

declare const divTest: React.ForwardRefExoticComponent<
    React.RefAttributes<HTMLDivElement>
>;

export const htmlTest: React.ForwardRefExoticComponent<
    React.RefAttributes<HTMLElement>
> = divTest;

Typescript 错误信息:

Type 'ForwardRefExoticComponent<RefAttributes<HTMLDivElement>>' is not assignable to type 'ForwardRefExoticComponent<RefAttributes<HTMLElement>>'.
  Types of parameters 'props' and 'props' are incompatible.
    Type 'RefAttributes<HTMLElement>' is not assignable to type 'RefAttributes<HTMLDivElement>'.

在最后一行中它指出我正在尝试将 RefAttributes<HTMLElement> 分配给 RefAttributes<HTMLDivElement>,但我以相反的方式进行分配(如第一行正确显示)。

我是不是错过了什么?因为我假设具有更具体的 HTMLDivElement 引用的组件可以分配给具有更通用的 HTMLElement 引用的组件。

可以由 forwardRef 转发的 ref 类型之一是回调引用。如果您处理过传递回调,那么您会知道 extends 在处理回调时有点“倒退”。但是forwardRef也可以转发一个RefObject.

由于您正在处理对象和回调的联合,因此您需要 exact 正确的引用类型。它不能更宽或更窄。

这里有一些例子来阐明它:

// ok
const f = (ref: (instance: HTMLDivElement | null) => void) => <div ref={ref}/>

// ok
const f = (ref: MutableRefObject<HTMLDivElement | null>) => <div ref={ref}/>

// ok because HTMLDivElement extends HTMLElement
const f = (ref: (instance: HTMLElement | null) => void) => <div ref={ref}/>

// error because HTMLElement is not assignable to HTMLDivElement
const f = (ref: MutableRefObject<HTMLElement | null>) => <div ref={ref}/>

// error because a div ref can only be assigned to a div
const f = (Element: "div" | "a", ref: MutableRefObject<HTMLDivElement | null>) => <Element ref={ref}/>

// error because we need a more exact ref type
const f = (Element: "div" | "a", ref: MutableRefObject<HTMLElement | null>) => <Element ref={ref}/>

// ok - the only possible ref here must be both a div and an a
const f = (Element: "div" | "a", ref: MutableRefObject<(HTMLDivElement & HTMLAnchorElement) | null>) => <Element ref={ref}/>

我原以为这是一个错误,但显然不是。

const f = (Element: "div" | "a", ref: (instance: (HTMLDivElement & HTMLAnchorElement) | null) => void) => <Element ref={ref}/>

所以现在我偶然发现了一个可能的解决方案,即您的 ref 可以是所有单个 ref 类型的交集。

如果不了解 HOC,很难说什么是最佳解决方案。