TroyAlford / react-jsx-parser

A React component which can parse JSX and output rendered React Components.
MIT License
640 stars 103 forks source link

Typescript compilation issues #291

Open arodidev opened 1 month ago

arodidev commented 1 month ago

I am getting some typescript issues that are a bit difficult to resolve when trying to override the components in the JSXParser components map prop. Sample code below:

<JSXParser
  components={{
    PieChart: (props: React.ComponentProps<typeof PieChart>) => (
      <ErrorBoundary FallbackComponent={ErrorFallback}>
        <PieChart
          {...(props as React.ComponentProps<typeof PieChart>)}
          container={
            CardComponent as React.ComponentType<ContainerComponentProps>
          }
        />
      </ErrorBoundary>
    ),    
  }}
  jsx={
    "<PieChart userOptions={{ dataLabels: { enabled: true }, stroke: { show: false } }} />"
  }
/>

I seem to be getting this error:

Type '(props: React.ComponentProps<typeof PieChart>) => JSX.Element' is not assignable to type 'ComponentType | ExoticComponent<{}>'

Is there a way to extend (or override) the types provided for the JSXParser component to fix these errors.

TroyAlford commented 1 month ago

Can you confirm that you are on the latest version? And which version of react you are using?

arodidev commented 1 month ago

I'm running version 2.0.0 of react-jsx-parser and version 18.3.3 of react . Should bumping the version sort out the issue?

TroyAlford commented 1 month ago

Hopefully. Please upgrade to 2.2.0 and see if that sorts it out?

arodidev commented 1 month ago

The problem still seems to persist

AndreasBrostrom commented 1 month ago

I'am experiencing this as well.

ERROR in src/components/Comp.tsx:764:40
TS2769: No overload matches this call.
  Overload 1 of 2, '(props: TProps): JsxParser', gave the following error.
    Type '({ children, ...props }: LinkProps) => Element' is not assignable to type 'ComponentType<{}> | ExoticComponent<{}>'.
      Type '({ children, ...props }: LinkProps) => Element' is not assignable to type 'FunctionComponent<{}>'.
        Types of parameters '__0' and 'props' are incompatible.
          Property 'to' is missing in type '{}' but required in type 'LinkProps'.
  Overload 2 of 2, '(props: TProps, context: any): JsxParser', gave the following error.
    Type '({ children, ...props }: LinkProps) => Element' is not assignable to type 'ComponentType<{}> | ExoticComponent<{}>'.
  > 764 |   const jsx = <JsxParser components={{ Link }} jsx={text} />;
        |                                        ^^^^
    765 |   console.log(jsx);
    766 |   return jsx;
    767 | };

Here is some extract from my code how i have it setup. In case i misunderstood something about this.

import React from "react";
import { isMobile } from "react-device-detect";
import { Link as RouterLink } from "react-router-dom";
import { colorNormal } from "./Styling";
import JsxParser from "react-jsx-parser";

interface LinkProps {
  children?: React.ReactNode | string;
  tooltip?: string;
  to: string;
  style?: React.CSSProperties;
}
export function Link({ children, ...props }: LinkProps) {
  const useTooltip = !isMobile && props.tooltip;

  return (
    <>
      {useTooltip && <Tooltip id="linkTooltip" />}
      <RouterLink
        data-tooltip-id={useTooltip ? "linkTooltip" : undefined}
        data-tooltip-content={useTooltip ? props.tooltip : undefined}
        data-tooltip-place={useTooltip ? "top" : undefined}
        to={props.to}
        rel="noopener noreferrer"
        style={{
          color: colorNormal,
          ...props.style,
        }}
      >
        {children}
      </RouterLink>
    </>
  );
}

export const handleText = (text: string) => {
  const jsx = <JsxParser components={{ Link }} jsx={text} />;
  return jsx;
};

From a render function:

export default Character;
function Page() {
  const description = [
    "Nulla non mauris mauris. Mauris vel maximus turpis.",
    'Sed rutrum urna quis ligula <Link to="/somewhere/here">egestas<Link>, a gravida justo venenatis.',
    'Phasellus sed tempus enim. In non malesuada tortor, sed <Link to="/somewhere/other">ullamcorper massa<Link>.',
  ];
  return (
    <>
      {description ? (
        description.map((body) => (
          <Text key={"body"}>{handleText(description.toString())}</Text>
        ))
      ) : (
        <Text>No information given.</Text>
      )}
    </>
  );
}
TroyAlford commented 3 weeks ago

@AndreasBrostrom I'm not 100% sure — but I believe the issue is that your system is interpreting the output of your component as JSX.Element. This may simply be the TS interpretation of () => <div /> type syntax.

It may help for the lib to allow for that as an alternative signature for components, but in the meantime, can you please change your code to:

export const Link = React.FC<LinkProps>({ children, ...props }) => {

The React.FC may type-coerce your TS into understanding that it will output a React.ReactNode rather than a JSX.Element. An alternative would be to alter the return statement as something like:

return <>
  {/* all your stuff */}
</> as React.ReactNode

and see if that addresses the coersion?

Finally, you can easily get around this as a workaround by simply adding:

// @ts-expect-error - incorrectly inferred return type

Right above the offending line.

TroyAlford commented 3 weeks ago

I attempted to expand the allowable signatures for components in the version I just pushed. Please pull latest and let me know if this resolves?