Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to extend Material UI ListItem?

I'm using TypeScript 3.4.5 and Material UI 4.2 and with the following piece of code:

interface MyItemProps {
    name: string;
    value: string;
}

function Item({ name, value, ...props }: ListItemProps<'li', MyItemProps>): ReactElement {
    return (
        <ListItem {...props} className="item">
            <ListItemText primary={name} secondary={value || '-'} />
        </ListItem>
    );
}

I'm getting Type 'boolean' is not assignable to type 'true' error. Why?

I've been looking into the type definitions for ListItem, but I can't figure out what's going on:

export interface ListItemTypeMap<P, D extends React.ElementType> {
  props: P & {
    alignItems?: 'flex-start' | 'center';
    autoFocus?: boolean;
    button?: boolean;
    ContainerComponent?: React.ElementType<React.HTMLAttributes<HTMLDivElement>>;
    ContainerProps?: React.HTMLAttributes<HTMLDivElement>;
    dense?: boolean;
    disabled?: boolean;
    disableGutters?: boolean;
    divider?: boolean;
    focusVisibleClassName?: string;
    selected?: boolean;
  };
  defaultComponent: D;
  classKey: ListItemClassKey;
}

declare const ListItem: OverridableComponent<ListItemTypeMap<{ button?: false }, 'li'>> &
  ExtendButtonBase<ListItemTypeMap<{ button: true }, 'div'>>;

export type ListItemClassKey =
  | 'root'
  | 'container'
  | 'focusVisible'
  | 'default'
  | 'dense'
  | 'disabled'
  | 'divider'
  | 'gutters'
  | 'button'
  | 'secondaryAction'
  | 'selected';

export type ListItemProps<D extends React.ElementType = 'li', P = {}> = OverrideProps<
  ListItemTypeMap<P, D>,
  D
>;

export default ListItem;

The only thing that comes to my mind is "type widening", but I don't really know what's happening and why.

Could somebody please explain what's going on? Most importantly, what is the correct way of extending a Material UI component?

like image 323
Honza Kalfus Avatar asked Oct 16 '19 16:10

Honza Kalfus


People also ask

What is stack in material UI?

The Stack component manages layout of immediate children along the vertical or horizontal axis with optional spacing and/or dividers between each child.

What is component prop in material ui?

According to https://mui.com/material-ui/api/button/ , it means "The component used for the root node. Either a string to use a HTML element or a component.".

How do you use props in MUI?

To be able to use props of such a MUI component on their own, props should be used with type arguments. Otherwise, the component prop will not be present in the props of the MUI component. The examples below use TypographyProps but the same will work for any component which has props defined with OverrideProps .

How do you import items in MUI?

import {} from '@material-ui/core'; import {} from '@material-ui/icons'; import {} from '@mui/material'; A better optimized approach, is to create a single file in your project where you import each component that you use individually, then export them all under a single namespace: // src/mui/index.


1 Answers

This appears to be a well established issue over on the material-ui github. Apparently it stems from using booleans to do union discrimination - I'm still new to TS so only have a general understanding of what that means!

The cleanest way to get past this which I found from those threads (without overwriting button with explicit any in the component props, ew) is by casting button as true directly on to the ListItem:

interface MyItemProps {
    name: string;
    value: string;
}

type MyListItem = ListItemProps<"li", MyItemProps>;

function Item({ name, value, button, ...props }: MyListItem): ReactElement {
    return (
        <ListItem {...props} className="item" button={button as true}>
            <ListItemText primary={name} secondary={value || '-'} />
        </ListItem>
    );
}
like image 57
lawrence-witt Avatar answered Oct 10 '22 20:10

lawrence-witt