Typescript如何正确推断React组件props中的泛型?

huangapple go评论70阅读模式
英文:

How can Typescript correctly infer generics in react component props?

问题

以下是您要翻译的部分:

只是为了上下文,我正在创建一个组件,如果子元素溢出,它会显示一个工具提示。 children 属性是一个渲染函数,因此调用此组件的人可以将 RefObject 分配给子组件的任何 ref 属性(例如:ref、itemRef、inputRef 等),以及应该调用的事件 onMouseEnter

import { TooltipProps } from '@mui/material';

export type OverflowTooltipProps = Omit<TooltipProps, 'children'> & {
    children: <RefElem extends HTMLElement>(
        ref: RefObject<RefElem>,
        onMouseEnter: () => void
    ) => ReactElement;
};

const OverflowTooltip = <RefElem extends HTMLElement>(props: OverflowTooltipProps) => {
    const { children, disableHoverListener, ...other } = props;
    const [overflowing, setOverflowing] = useState(false);
    const childrenRef = useRef<RefElem>(null);

    const testIfOverflowing = () => {
        const scrollWidth = childrenRef.current?.scrollWidth;
        const clientWidth = childrenRef.current?.clientWidth;
        if (scrollWidth && clientWidth) {
            setOverflowing(scrollWidth > clientWidth);
        }
    };

    useEffect(() => {
        testIfOverflowing();
        window.addEventListener('resize', testIfOverflowing);
        return () => window.removeEventListener('resize', testIfOverflowing);
    }, [children]);

    return (
        <Tooltip disableHoverListener={disableHoverListener || !overflowing} {...other}>
            {children<RefElem>(childrenRef, testIfOverflowing)}
        </Tooltip>
    );
};

这是一个(简化的)用例:

<OverflowTooltip title={attachment.file.name}>
    {(ref, onMouseEnter) => (
        <Chip ref={ref} label={attachment.file.name} onMouseEnter={onMouseEnter} />
    )}
</OverflowTooltip>

但是,我收到以下错误:

Typescript如何正确推断React组件props中的泛型?

我做错了什么?这不应该根据使用的泛型正确推断吗?

英文:

Just for context, I'm creating a component that shows a Tooltip if the child element is overflowing. children prop is a render function so whoever calls this component can assign the RefObject to whichever ref prop of the child component it wants (i.e.: ref, itemRef, inputRef,..), as well as the event that should be called onMouseEnter.

import { TooltipProps } from &#39;@mui/material&#39;;

export type OverflowTooltipProps = Omit&lt;TooltipProps, &#39;children&#39;&gt; &amp; {
	children: &lt;RefElem extends HTMLElement&gt;(
		ref: RefObject&lt;RefElem&gt;,
		onMouseEnter: () =&gt; void
	) =&gt; ReactElement;
};

const OverflowTooltip = &lt;RefElem extends HTMLElement&gt;(props: OverflowTooltipProps) =&gt; {
	const { children, disableHoverListener, ...other } = props;
	const [overflowing, setOverflowing] = useState(false);
	const childrenRef = useRef&lt;RefElem&gt;(null);

	const testIfOverflowing = () =&gt; {
		const scrollWidth = childrenRef.current?.scrollWidth;
		const clientWidth = childrenRef.current?.clientWidth;
		if (scrollWidth &amp;&amp; clientWidth) {
			setOverflowing(scrollWidth &gt; clientWidth);
		}
	};

	useEffect(() =&gt; {
		testIfOverflowing();
		window.addEventListener(&#39;resize&#39;, testIfOverflowing);
		return () =&gt; window.removeEventListener(&#39;resize&#39;, testIfOverflowing);
	}, [children]);

	return (
		&lt;Tooltip disableHoverListener={disableHoverListener || !overflowing} {...other}&gt;
			{children&lt;RefElem&gt;(childrenRef, testIfOverflowing)}
		&lt;/Tooltip&gt;
	);
};

This is a (simplified) use case:

&lt;OverflowTooltip title={attachment.file.name}&gt;
	{(ref, onMouseEnter) =&gt; (
		&lt;Chip ref={ref} label={attachment.file.name} onMouseEnter={onMouseEnter} /&gt;
	)}
&lt;/OverflowTooltip&gt;

However, I get the following error:

Typescript如何正确推断React组件props中的泛型?

What am I doing wrong? Shouldn't this be inferred correctly because of the generics being used?

答案1

得分: 2

这是您要翻译的代码部分:

import { Chip, Tooltip, TooltipProps } from "@mui/material";
import { ReactElement, RefObject, useEffect, useRef, useState } from "react";

export type OverflowTooltipProps<RefElem extends HTMLElement> = Omit<
  TooltipProps,
  "children"
> & {
  children: (ref: RefObject<RefElem>, onMouseEnter: () => void) => ReactElement;
};

const OverflowTooltip = <RefElem extends HTMLElement>(
  props: OverflowTooltipProps<RefElem>
) => {
  const { children, disableHoverListener, ...other } = props;
  const [overflowing, setOverflowing] = useState(false);
  const childrenRef = useRef<RefElem>(null);

  const testIfOverflowing = () => {
    const scrollWidth = childrenRef.current?.scrollWidth;
    const clientWidth = childrenRef.current?.clientWidth;
    if (scrollWidth && clientWidth) {
      setOverflowing(scrollWidth > clientWidth);
    }
  };

  useEffect(() => {
    testIfOverflowing();
    window.addEventListener("resize", testIfOverflowing);
    return () => window.removeEventListener("resize", testIfOverflowing);
  }, [children]);

  return (
    <Tooltip
      disableHoverListener={disableHoverListener || !overflowing}
      {...other}
    >
      {children(childrenRef, testIfOverflowing)}
    </Tooltip>
  );
};

const attachment = {
  file: {
    name: "test",
  },
};

const App = () => {
  <OverflowTooltip<HTMLDivElement> title={attachment.file.name}>
    {(ref, onMouseEnter) => (
      <Chip
        ref={ref}
        label={attachment.file.name}
        onMouseEnter={onMouseEnter}
      />
    )}
  </OverflowTooltip>;
};

如果您需要进一步的帮助,请告诉我。

英文:

You actually have to pass generic into to type also. Here you have working solution:

import { Chip, Tooltip, TooltipProps } from &quot;@mui/material&quot;;
import { ReactElement, RefObject, useEffect, useRef, useState } from &quot;react&quot;;

export type OverflowTooltipProps&lt;RefElem extends HTMLElement&gt; = Omit&lt;
  TooltipProps,
  &quot;children&quot;
&gt; &amp; {
  children: (ref: RefObject&lt;RefElem&gt;, onMouseEnter: () =&gt; void) =&gt; ReactElement;
};

const OverflowTooltip = &lt;RefElem extends HTMLElement&gt;(
  props: OverflowTooltipProps&lt;RefElem&gt;
) =&gt; {
  const { children, disableHoverListener, ...other } = props;
  const [overflowing, setOverflowing] = useState(false);
  const childrenRef = useRef&lt;RefElem&gt;(null);

  const testIfOverflowing = () =&gt; {
    const scrollWidth = childrenRef.current?.scrollWidth;
    const clientWidth = childrenRef.current?.clientWidth;
    if (scrollWidth &amp;&amp; clientWidth) {
      setOverflowing(scrollWidth &gt; clientWidth);
    }
  };

  useEffect(() =&gt; {
    testIfOverflowing();
    window.addEventListener(&quot;resize&quot;, testIfOverflowing);
    return () =&gt; window.removeEventListener(&quot;resize&quot;, testIfOverflowing);
  }, [children]);

  return (
    &lt;Tooltip
      disableHoverListener={disableHoverListener || !overflowing}
      {...other}
    &gt;
      {children(childrenRef, testIfOverflowing)}
    &lt;/Tooltip&gt;
  );
};

const attachment = {
  file: {
    name: &quot;test&quot;,
  },
};

const App = () =&gt; {
  &lt;OverflowTooltip&lt;HTMLDivElement&gt; title={attachment.file.name}&gt;
    {(ref, onMouseEnter) =&gt; (
      &lt;Chip
        ref={ref}
        label={attachment.file.name}
        onMouseEnter={onMouseEnter}
      /&gt;
    )}
  &lt;/OverflowTooltip&gt;;
};

huangapple
  • 本文由 发表于 2023年7月4日 20:22:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76612583.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定