Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material-UI: How to declare a type for React.ComponentType<P> with typescript

I am using Typescript and Material-UI I want to declare the component type for a variable like this

import MoreVert from '@material-ui/icons/MoreVert'
import { SvgIconProps } from '@material-ui/core/SvgIcon';

let myIcon: SvgIconProps = <MoreVert />; // does not work

But I am getting the error:

[ts]
Type 'Element' is not assignable to type 'SvgIconProps'.
  Types of property 'type' are incompatible.
    Type 'string | ComponentClass<any> | StatelessComponent<any>' is not assignable to type 'string'.
      Type 'ComponentClass<any>' is not assignable to type 'string'.

This is how the SvgIcon.ts looks like. What am I doing wrong?

import * as React from 'react';
import { StandardProps, PropTypes } from '..';

export interface SvgIconProps
  extends StandardProps<React.SVGProps<SVGSVGElement>, SvgIconClassKey> {
  color?: PropTypes.Color | 'action' | 'disabled' | 'error';
  component?: React.ReactType<SvgIconProps>;
  fontSize?: 'inherit' | 'default' | 'small' | 'large';
  nativeColor?: string;
  titleAccess?: string;
  viewBox?: string;
}

export type SvgIconClassKey =
  | 'root'
  | 'colorSecondary'
  | 'colorAction'
  | 'colorDisabled'
  | 'colorError'
  | 'colorPrimary'
  | 'fontSizeInherit'
  | 'fontSizeSmall'
  | 'fontSizeLarge';

declare const SvgIcon: React.ComponentType<SvgIconProps>;

export default SvgIcon;
like image 856
Murat Karagöz Avatar asked Sep 27 '18 12:09

Murat Karagöz


3 Answers

Reference: https://www.typescriptlang.org/docs/handbook/jsx.html

By default the result of a JSX expression is typed as any. You can customize the type by specifying the JSX.Element interface. However, it is not possible to retrieve type information about the element, attributes or children of the JSX from this interface. It is a black box.

The reason why I lose all type information with JSX.Element is because it extends React.ReactElement<any> which has the type of any. To fix this I used it like this

 let myIcon: React.ReactElement<SvgIconProps> = <MoreVert />; 

Now I have the element with all the type information.

like image 135
Murat Karagöz Avatar answered Sep 28 '22 08:09

Murat Karagöz


As already briefly described by Ebuall in a comment, here's how you would declare the variable depending on whether you want the JSX element or the component type:

let myIconElement: JSX.Element = <MoreVert />;
let MyIconComponent: React.ComponentType<SvgIconProps> = MoreVert;

// Example component that uses the icon
function myComponent(props: {}) {
    return <>
        {myIconElement}
        {<MyIconComponent />}
    </>;
}
like image 33
Matt McCutchen Avatar answered Sep 28 '22 08:09

Matt McCutchen


If anyone does not work by the way answered question of @Murat Karagöz you may try this when initializing an interface or a variable of an Icons from Material UI using TypeScript.

I got this working by declaring like this:

import ShowChartIconOutlined from '@material-ui/icons/ShowChartOutlined';
import { SvgIconProps } from '@material-ui/core';

type Menu = {
  id: number;
  icon: (props: SvgIconProps) => JSX.Element;
  label: string;
}[];

const NavBarMenus: Menu = [
  {
    id: 1,
    icon: ShowChartIconOutlined,
    label: 'Dashboard',
  },
...
];

Reference: [Docs & Types] Importing icons from @material-ui/icons in TypeScript #647

like image 44
Sauer Voussoir Avatar answered Sep 28 '22 08:09

Sauer Voussoir