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
,很难说什么是最佳解决方案。
我正在尝试编写一个 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
,很难说什么是最佳解决方案。