title: Intersection Observer in Typescript throws error in useRef
body: <p>I have here a text animation that is working perfect. What I want to add now is an Intersection Observer so that the animation only starts once I scroll down to the Box.</p>
  <p>So what I did to achieve this is:
  I used the react hook <code>useRef</code> to use as reference to the element I want to observe and applied it to my Box with <code>ref={containerRef}</code>. Then declared a callback function that receives an array of IntersectionObserverEntries as a parameter, inside this function I take the first and only entry and check if it is intersecting with the viewport and if it is then it calls setIsVisible with the value of entry.isIntersecting (true/false). After that I added the react hook useEffect and created an observer contructor using the callback function and the options I just created before. I implemented the logic in a new hook that I called <code>useElementOnscreen</code></p>
  <p>But Typescript is telling me an error at <code>containerRef?.current</code>:</p>
  <pre><code>Argument of type 'IntersectionObserver' is not assignable to parameter of type 'Element'.
    Type 'IntersectionObserver' is missing the following properties from type 'Element': attributes, classList, className, clientHeight, and 160 more.
  </code></pre>
  <p>And I am not sure how to solve this error. I think this is also the reason that my <code>ref={containerRef}</code> is throwing an error too</p>
  <pre><code>The expected type comes from property 'ref' which is declared here on type 'IntrinsicAttributes &amp; { component: ElementType&lt;any&gt;; } &amp; SystemProps&lt;Theme&gt; &amp; { children?: ReactNode; component?: ElementType&lt;...&gt; | undefined; ref?: Ref&lt;...&gt; | undefined; sx?: SxProps&lt;...&gt; | undefined; } &amp; CommonProps &amp; Omit&lt;...&gt;'
  </code></pre>
  <p><strong>The animation:</strong>
  So, TopAnimateBlock and BottomAnimateBlock have numOfLine property hence how many lines is inside the block. The second property in BottomAnimateBlock is delayTopLine, it should have the same numbers as a numOfLine in TopAnimateBlock, because we need to wait for top lines to play.</p>
  <p><code>TextAnimation.tsx</code></p>
  <pre><code>import { Box, Stack, Typography } from '@mui/material';
  import React, { useRef, useEffect, useState } from 'react';
  import styled, { keyframes } from 'styled-components';
  
  const showTopText = keyframes`
    0% { transform: translate3d(0, 100% , 0); }
    40%, 60% { transform: translate3d(0, 50%, 0); }
    100% { transform: translate3d(0, 0, 0); }
  `;
  const showBottomText = keyframes`
    0% { transform: translate3d(0, -100%, 0); }
    100% { transform: translate3d(0, 0, 0); }
  `;
  
  const Section = styled.section`
    width: calc(100% + 10vmin);
    display: flex;
    flex-flow: column;
    padding: 2vmin 0;
    overflow: hidden;
    &amp;:last-child {
      border-top: 1vmin solid white;
    }
  `;
  
  const Block = styled.div&lt;{ numOfLine: number }&gt;`
    position: relative;
  `;
  const TopAnimateBlock = styled(Block)`
    animation: ${showTopText} calc(0.5s * ${props =&gt; props.numOfLine}) forwards;
    animation-delay: 0.5s;
    transform: translateY(calc(100% * ${props =&gt; props.numOfLine}));
  `;
  const BottomAnimateBlock = styled(Block)&lt;{ delayTopLine: number }&gt;`
    animation: ${showBottomText} calc(0.5s * ${props =&gt; props.numOfLine}) forwards;
    animation-delay: calc(0.7s * ${props =&gt; props.delayTopLine});
    transform: translateY(calc(-100% * ${props =&gt; props.numOfLine}));
  `;
  
  const TextStyle = styled.p&lt;{ color: string }&gt;`
    font-family: Roboto, Arial, sans-serif;
    font-size: 12vmin;
    color: ${props =&gt; props.color};
  `;
  
  
  const useElementOnScreen = (options) =&gt; {
      const containerRef = useRef&lt;IntersectionObserver | null&gt;(null);
      const [isVisible, setIsVisible] = useState(false);
  
      const callbackFunction = (entries) =&gt; {
          const [entry] = entries;
          setIsVisible(entry.isIntersecting);
      };
  
      useEffect(() =&gt; {
          const observer = new IntersectionObserver(callbackFunction, options);
  
          if (containerRef.current) observer.observe(containerRef?.current);
  
          return () =&gt; {
              if (containerRef.current) observer.unobserve(containerRef?.current);
          };
      }, [containerRef, options]);
  
      return [containerRef, isVisible];
  };
  
  export function Details() {
      const [containerRef, isVisible] = useElementOnScreen({
          root: null,
          rootMargin: '0px',
          threshold: 1.0,
      });
  
      return (
      &lt;&gt;
      &lt;Typography&gt;Scroll Down&lt;/Typography&gt;
       &lt;Box ref={containerRef}&gt;
        &lt;Section&gt;
          &lt;TopAnimateBlock numOfLine={2}&gt;
            &lt;TextStyle color=&quot;grey&quot;&gt;mimicking&lt;/TextStyle&gt;
            &lt;TextStyle color=&quot;white&quot;&gt;apple's design&lt;/TextStyle&gt;
          &lt;/TopAnimateBlock&gt;
        &lt;/Section&gt;
        &lt;Section&gt;
          &lt;BottomAnimateBlock numOfLine={1} delayTopLine={2}&gt;
            &lt;TextStyle color=&quot;white&quot;&gt;for the win!&lt;/TextStyle&gt;
          &lt;/BottomAnimateBlock&gt;
        &lt;/Section&gt;
      &lt;/Box&gt;
  &lt;/&gt;
    );
  };
  </code></pre>