扩展Material UI ListItem的正确方法是什么?

时间:2019-10-16 16:16:00

标签: javascript reactjs typescript material-ui

我正在使用TypeScript 3.4.5和Material UI 4.2,并使用以下代码:

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>
    );
}

我遇到Type 'boolean' is not assignable to type 'true'错误。为什么?

我一直在研究ListItem的类型定义,但我不知道发生了什么事情:

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;

我唯一想到的是“类型扩展”,但我真的不知道发生了什么以及为什么。

有人可以解释发生了什么吗?最重要的是,扩展Material UI组件的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

这似乎是 material-ui github 上的 well established issue。显然它源于使用布尔值进行联合歧视 - 我对 TS 还是个新手,所以只对这意味着什么有一个大致的了解!

我从这些线程中发现的最干净的方法是通过直接将 button 强制转换为 true到any

button

答案 1 :(得分:1)

这一切都归结为 intersection types 的工作原理。

基本上,您可以拥有一个或另一个:

  • 如果元素类型为“li”,则按钮属性必须设置为false或undefined
  • 如果元素类型为“div”,则按钮属性必须设置为 true。

因此,就我而言,因为我使用“li”作为具有 ListItemProps 泛型类型的类型参数,所以我不能只传递 button 道具。它必须被省略(或者,设置为 false):

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

function Item({ name, value, button /* omit the prop, don't pass it down to ListItem */, ...props }: ListItemProps<'li', MyItemProps>): ReactElement {
    return (
        <ListItem {...props} className="item">
            <ListItemText primary={name} secondary={value || '-'} />
        </ListItem>
    );
}