Búsqueda de sitios web

javascript - Especificar props específicos y aceptar props generales de HTML en una aplicación React con Typescript


Tengo un Componente Wrapper de React, que acepta algunas props, pero reenvía todas las demás al componente hijo (especialmente relevante para props nativas como className, id, etc.).

Sin embargo, Typescript se queja cuando paso props nativos. Ve el mensaje de error:

TS2339: La propiedad 'className' no existe en el tipo 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly'.

¿Cómo puedo obtener un componente con props específicas que también acepte props nativas (sin aceptar ningún prop y renunciar a la verificación de tipos)?

Mi código se ve así:

interface WrapperProps extends JSX.IntrinsicAttributes {
  callback?: Function
}

export class Wrapper extends React.Component<WrapperProps>{
  render() {
    const { callback, children, ...rest } = this.props;
    return <div {...rest}>
      {children}
    </div>;
  }
}

export const Test = () => {
  return <Wrapper className="test">Hi there</Wrapper>
}

Para tu información: encontré una pregunta similar aquí, pero la respuesta básicamente abandona la verificación de tipos, lo cual quiero evitar: Enlace a la pregunta de SO

Respuesta #1

Podemos ver cómo se definen las propiedades de div:

interface IntrinsicElements {
    div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
}

Si usamos React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> como el tipo base, tendremos todas las propiedades de div. Dado que DetailedHTMLProps solo añade ref a React.HTMLAttributes<HTMLDivElement>, podemos usar solo esto como la interfaz base para obtener todas las propiedades de div:

interface WrapperProps extends React.HTMLAttributes<HTMLDivElement> {
  callback?: Function
}

export class Wrapper extends React.Component<WrapperProps>{
  render() {
    const { callback, children, ...rest } = this.props;
    return <div {...rest}>
      {children}
    </div>;
  }
}

export const Test = () => {
  return <Wrapper className="test">Hi there</Wrapper> // works now
}

Respuesta #2

Echa un vistazo a ComponentProps, ComponentPropsWithRef y ComponentPropsWithoutRef: esto aceptará una entrada genérica que puede ser "div", "button" o cualquier otro componente. También incluirá props específicos de React como className:

import React, {
  forwardRef,
  ComponentPropsWithoutRef,
  ComponentProps,
  ComponentPropsWithRef
} from "react";

const ExampleDivComponent = forwardRef<
  HTMLDivElement,
  ComponentPropsWithoutRef<"div">
>(({ children, ...props }, ref) => {
  return (
    <div {...props} ref={ref}>
      {children}
    </div>
  );
});

<ExampleDivComponent
  className=""
  style={{ background: "green" }}
  tabIndex={0}
  onTouchStart={() => alert("touched")}
/>;

const ExampleButtonComponent: React.FC<ComponentProps<"button">> = ({
  children,
  ...props
}) => {
  return <button {...props}>{children}</button>;
};

<ExampleButtonComponent onClick={() => alert("clicked")} />;

Artículos relacionados