简体   繁体   中英

Generics error with forwardRef: Property 'ref' does not exist on type 'IntrinsicAttributes'

When using forwardRef with generics, I get Property 'children' does not exist on type 'IntrinsicAttributes' or Property 'ref' does not exist on type 'IntrinsicAttributes' .

https://codesandbox.io/s/react-typescript-0dt6d?fontsize=14

Relevant code in CodeSandbox link above replicated here:

interface SimpleProps<T extends string>
  extends React.HTMLProps<HTMLButtonElement> {
  random: T;
}

interface Props {
  ref?: React.RefObject<HTMLButtonElement>;
  children: React.ReactNode;
}

function WithGenericsButton<T extends string>() {
  return React.forwardRef<HTMLButtonElement, Props & SimpleProps<T>>(
    ({ children, ...otherProps }, ref) => (
      <button ref={ref} className="FancyButton" {...otherProps}>
        {children}
      </button>
    )
  );
}

() => (
  <WithGenericsButton<string> ref={ref} color="green">
    Click me! // Errors: Property 'children' does not exist on type 'IntrinsicAttributes'
  </WithGenericsButton>
)

A potential solution is suggested here but not sure how to implement in this context: https://github.com/microsoft/TypeScript/pull/30215 (Found from https://stackoverflow.com/a/51898192/9973558 )

So the main problem here is that you're returning the result of React.forwardRef in your render, which isn't a valid return type for the render func. You'd need to define the forwardRef result as it's own component, and then render that inside your WithGenericsButton higher order component, like so:

import * as React from "react";

interface SimpleProps<T extends string> {
  random: T;
}

interface Props {
  children: React.ReactNode;
  color: string;
}

function WithGenericsButton<T extends string>(
  props: Props & SimpleProps<T> & { ref: React.Ref<HTMLButtonElement> }
) {
  type CombinedProps = Props & SimpleProps<T>;
  const Button = React.forwardRef<HTMLButtonElement, CombinedProps>(
    ({ children, ...otherProps }, ref) => (
      <button ref={ref} className="FancyButton" {...otherProps}>
        {children}
      </button>
    )
  );
  return <Button {...props} />;
}

const App: React.FC = () => {
  const ref = React.useRef<HTMLButtonElement>(null);
  return (
    <WithGenericsButton<string> ref={ref} color="green" random="foo">
      Click me!
    </WithGenericsButton>
  );
};

If you put that in a sandbox or playground you'll see that props is now typed correctly including a random prop of T

The problem that you are getting is because of this function:

function WithGenericsButton<T extends string>() {
  return React.forwardRef<HTMLButtonElement, Props & SimpleProps<T>>(
    ({ children, ...otherProps }, ref) => (
      <button ref={ref} className="FancyButton" {...otherProps}>
        {children}
      </button>
    )
  );
}

WithGenericsButton is not a component. It is a js function that returns a component. TS is basically telling you: hey the component WithGenericsButton (because you are using it as one) doesn't have a prop called children and it is right, it doesn't.

In your case to get a component that you can render you'll need to do something like this: const StringButton = WithGenericsButton<string>();

I found another good way to figure out your question. Something like this,

export interface Props = {
   ...yourPropsHere;
};

export interface CompoundedComponent extends React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLInputElement>> {
   yourStaticFunctionOrSomethingLikeThat: () => void;
}

const Component = React.forwardRef<HTMLInputElement, Props>((props, ref) => (
    <input ref={ref} {...props} />
)) as CompoundedComponent;

Component.yourStaticFunctionOrSomethingLikeThat = () => {};

A convenient alternative that worked for me, (no typescript expert at all)

const ReusableComponent = React.forwardRef((props: { [key: string]: unknown }, ref) => {
  const { ref, children, ...moreProps } = props
  return (
    <Something ref={ref} {...moreProps}>
      {children}
    </Something>
  )
})
export default ReusableComponent

found convenient to use [key: string]: unknown when is not possible to know what props are you receiving.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM