【问题标题】:What is the correct way to extend Material UI ListItem?扩展 Material UI ListItem 的正确方法是什么?
【发布时间】:2020-02-13 11:48:50
【问题描述】:

我正在使用 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 组件的正确方法是什么?

【问题讨论】:

    标签: javascript reactjs typescript material-ui


    【解决方案1】:

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

    我从这些线程中找到的最干净的方法是通过将button 直接转换为ListItem 上的真实any 覆盖button

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

    【讨论】:

    • 我现在对 TS 的理解比我问这个问题时的理解要好。你几乎是对的 - 它与交叉路口类型有关。基本上,如果元素类型是“li”,那么 button 属性必须设置为 false 或 undefined。如果元素类型是“div”,那么 button 属性必须设置为 true。考虑到所有这些,我认为将 button 属性转换为 true 是不正确的,仅供参考 :) 根本不应该设置它。
    • 啊,好吧,这更有意义。我偶然发现这个问题的方式是,我试图创建一个包装组件,它对ListItem 将创建的元素类型感到矛盾,这意味着我想将它传递给componentbutton 道具,具体取决于上下文。我最初的答案似乎仍然是实现这一目标的最佳方法,尽管我知道铸造 button 并不理想!可能这本质上也只是 MUI 告诉我,如果我想保持类型安全,就不要尝试这样做,这也是一个公平的结论,也是我必须考虑的事情。
    【解决方案2】:

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

    基本上,你可以有一个,或者另一个:

    • 如果元素类型是“li”,那么按钮属性必须设置为 false 或 undefined
    • 如果元素类型是“div”,那么 button 属性必须设置为 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>
        );
    }
    

    【讨论】:

      猜你喜欢
      • 2020-08-18
      • 1970-01-01
      • 1970-01-01
      • 2017-09-14
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多