Nicholas Rice
Nicholas Rice

Reputation: 1

How do you type the refObject of a dynamic tag element in React Typescript?

This issue keeps reoccurring and I always seem to code around it so I'm hoping someone out there can help me shed some light on the correct way to type this.

function MyComponent({ isCondition }): Props { {
  const ElementType: React.ElementType = isCondition ? 'a' : 'div'
  const ref = useRef<HTMLAnchorElement | HTMLDivElement>(null)

return (
  <ElementType ref={ref}>
    {/* other stuff */}
  </ElementType>
)
}

I keep getting the typescript error:

Type 'RefObject<HTMLAnchorElement | HTMLDivElement>' is not assignable to type '((string | ((instance: HTMLAnchorElement | null) => void) | RefObject<HTMLAnchorElement>) & (string | RefObject<HTMLDivElement> | ((instance: HTMLDivElement | null) => void))) | null | undefined'.
  Type 'RefObject<HTMLAnchorElement | HTMLDivElement>' is not assignable to type 'RefObject<HTMLAnchorElement> & ((instance: HTMLDivElement | null) => void)'.
    Type 'RefObject<HTMLAnchorElement | HTMLDivElement>' is not assignable to type 'RefObject<HTMLAnchorElement>'.
      Type 'HTMLAnchorElement | HTMLDivElement' is not assignable to type 'HTMLAnchorElement'.
        Type 'HTMLDivElement' is missing the following properties from type 'HTMLAnchorElement': charset, coords, download, hreflang, and 21 more.ts(2322)

I can't seem to type so that Typescript understands the type of the ref will match the JSX tag.

I saw another post suggesting that with refs, information flows backwards and that the only way to configure this so typescript won't complain is type assertions or leaning on the ref callback option.

<ElementType ref={node => ref.current = node}>

this produces the error

Cannot assign to 'current' because it is a read-only property.ts(2540)

and the node param also complains that it implicitly type "any"

Can anyone explain what typing is wrong or how I'm looking at this wrong?

Upvotes: 0

Views: 42

Answers (1)

divan_1920
divan_1920

Reputation: 11

You could try something like this:

import React, { useRef, ElementType as ElementTypeProp } from 'react';

type Props<T extends ElementTypeProp> = {
  isCondition: boolean;
  as?: T;
};

const MyComponent = <T extends ElementTypeProp = 'div'>({ isCondition, as }: Props<T>) => {
  const ElementType = as || (isCondition ? 'a' : 'div');
  const ref = useRef<T extends 'a' ? HTMLAnchorElement : HTMLDivElement>(null);

  return <ElementType ref={ref}>Hello</ElementType>;
};

export default MyComponent;

Upvotes: 0

Related Questions