Search code examples
javascriptreactjstypescriptsvg

React typescript with SVG element


I'm keep getting a error for my component with a icon Map for the icon elements, error message below for my code. and I try SVGProps<SVGSVGElement> | Element; define the type by search around, I still getting red underscore SvgIconProps of the type error message.

Type '({ icon, width, height, fill, className, }: SvgIconProps) => string | Element | JSX.Element | SVGProps<SVGSVGElement>' is not assignable to type 'FC<SvgIconProps>'.
  Type 'string | Element | JSX.Element | SVGProps<SVGSVGElement>' is not assignable to type 'ReactNode'.
    Type 'Element' is not assignable to type 'ReactNode'.
      Type 'Element' is missing the following properties from type 'ReactPortal': type, props, key

Code here in the below and code sandbox here

import React, { SVGProps } from "react";

type iconListType = {
  [key: string]: string | SVGProps<SVGSVGElement> | Element;
};

export const iconList: iconListType = {
  fillOn:
    "M8 2H16C17.6569 2 19 3.34315 19 5V19C19 20.6569 17.6569 22 16 22H8C6.34315 22 5 20.6569 5 19V5C5 3.34315 6.34315 2 8 2ZM16 20.5C16.8284 20.5 17.5 19.8284 17.5 19V5C17.5 4.17157 16.8284 3.5 16 3.5H8C7.17157 3.5 6.5 4.17157 6.5 5V19C6.5 19.8284 7.17157 20.5 8 20.5H16ZM12.5 5H8V10H12.5V5ZM9.51 8.5H11V6.5H9.51V8.5ZM8 14.5H16V16H8V14.5ZM16 17.5H8V19H16V17.5ZM16 10H14V8.51H16V10ZM16 11.5H8V13H16V11.5Z",
  ,
  ads: (
    <svg
      width="16"
      height="16"
      viewBox="0 0 24 24"
      fill="none"
      className="pr-1"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M10.75 15.4999C11.1642 15.4999 11.5 15.1641 11.5 14.7499C11.5 14.3356 11.1642 13.9999 10.75 13.9999C10.3358 13.9999 10 14.3356 10 14.7499C10 15.1641 10.3358 15.4999 10.75 15.4999Z"
        fill="#F6F6F9"
      />
      <path
        d="M19.27 8.99985L14.16 5.99985C13.9906 5.90762 13.7922 5.88421 13.6059 5.9345C13.4197 5.98478 13.26 6.10488 13.16 6.26985C13.0652 6.43872 13.0404 6.63801 13.0909 6.82497C13.1414 7.01192 13.2631 7.17164 13.43 7.26985L18.54 10.2699C18.8478 10.4443 19.1038 10.6972 19.282 11.0029C19.4601 11.3086 19.5539 11.6561 19.5539 12.0099C19.5539 12.3636 19.4601 12.7111 19.282 13.0168C19.1038 13.3225 18.8478 13.5754 18.54 13.7499L7.54003 20.2299C7.23599 20.4054 6.8911 20.4978 6.54003 20.4978C6.18895 20.4978 5.84406 20.4054 5.54003 20.2299C5.23482 20.0536 4.98159 19.7999 4.80598 19.4944C4.63037 19.1888 4.53863 18.8423 4.54003 18.4899V5.48985C4.53863 5.13743 4.63037 4.7909 4.80598 4.48534C4.98159 4.17979 5.23482 3.92606 5.54003 3.74985C5.84406 3.57431 6.18895 3.4819 6.54003 3.4819C6.8911 3.4819 7.23599 3.57431 7.54003 3.74985L10 5.24985V11.9999C10 12.1988 10.079 12.3895 10.2197 12.5302C10.3603 12.6708 10.5511 12.7499 10.75 12.7499C10.9489 12.7499 11.1397 12.6708 11.2804 12.5302C11.421 12.3895 11.5 12.1988 11.5 11.9999V4.81985C11.4996 4.6888 11.4653 4.56008 11.4005 4.44619C11.3357 4.33229 11.2425 4.2371 11.13 4.16985L8.26003 2.48985C7.73118 2.17856 7.12869 2.0144 6.51503 2.0144C5.90136 2.0144 5.29887 2.17856 4.77003 2.48985C4.23518 2.79278 3.78983 3.23159 3.47902 3.76189C3.16821 4.2922 3.00297 4.89518 3.00003 5.50985V18.5099C2.99767 19.1296 3.16039 19.7388 3.47147 20.2748C3.78255 20.8109 4.23076 21.2544 4.77003 21.5599C5.29887 21.8711 5.90136 22.0353 6.51503 22.0353C7.12869 22.0353 7.73118 21.8711 8.26003 21.5599L19.26 15.0799C19.7866 14.7685 20.2229 14.3254 20.526 13.794C20.8291 13.2627 20.9885 12.6616 20.9885 12.0499C20.9885 11.4381 20.8291 10.837 20.526 10.3057C20.2229 9.77435 19.7866 9.33118 19.26 9.01985L19.27 8.99985Z"
        fill="#F6F6F9"
      />
    </svg>
  ),
};

interface SvgIconProps {
  icon: string;
  fill?: string;
  className: string;
  width?: number;
  height?: number;
}

export const SvgIcon: React.FC<SvgIconProps> = ({
  icon = "",
  width = 16,
  height = 16,
  fill,
  className,
}) => {
  const iconCheck = typeof iconList[icon] === "string";
  return icon && iconCheck ? (
    <svg
      width={width}
      height={height}
      fill={fill}
      viewBox="0 0 24 24"
      className={className}
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        fill-rule="evenodd"
        clip-rule="evenodd"
        d={iconList[icon]}
        fill={fill}
      />
    </svg>
  ) : (
    iconList[icon]
  );
};

Thanks in advance!


Solution

  • SVGProps<SVGSVGElement> is not a type you should probably use directly.

    If you render an <svg> tag it returns JSX.Element

    const test = <svg />
    //    ^? JSX.Element
    

    And your icon list also contains strings, so the type should be:

    type iconListType = {
      [key: string]: string | JSX.Element;
    };
    

    See Playground


    Fixing that shows another, somewhat unrelated problem:

    d={iconList[icon]}
    
    Type 'string | Element' is not assignable to type 'string | undefined'.
      Type 'Element' is not assignable to type 'string'.(2322)
    

    Here you are not narrowing the type of iconList[icon] correctly.

    First you need assign it a value that can be narrowed:

    const iconContent = iconList[icon]
    

    Then you can narrow that value:

    typeof iconContent === 'string'
      ? //... use as string ...
      : //... use as JSX.Element ...
    

    See Playground with no type errors.