【问题标题】:Extend material-ui component in typescript在 typescript 中扩展 material-ui 组件
【发布时间】:2021-08-10 23:04:45
【问题描述】:

尝试扩展 Material-ui Button 组件以添加新的道具。

目的是添加一个新的道具:fontSize,它有三个选项 - smallmediumlarge

<Button variant="outlined" color="primary" fontSize="small">
    button_small
</Button>

并在 css 中使用它来进行所需的更改。

根据typescript theme customisation 的材料 ui 文档,我已经自定义了主题并且效果很好。

唯一的问题是尝试更新 Button 的 prop 类型不起作用。

我得到这个错误是因为没有重载匹配,这很明显,因为材料 ui 按钮组件不知道“fontSize”新道具。

错误 TS2769:没有重载匹配此调用。

重载 1 of 3, '(props: { href: string; } & { children?: ReactNode; 颜色?:颜色 |不明确的;禁用?:布尔值 |不明确的; disableElevation?: 布尔值 |不明确的; disableFocusRipple?: 布尔值 | 不明确的; ... 5 更多 ...;变种?:“文本”| ... 2 更多 ... | 不明确的; } & { ...; } & CommonProps<...> & Pick<...>): 元素', 给出了以下错误。输入'{孩子:字符串;变体: “概述”;颜色:“主要”;字体大小:字符串; }' 不可赋值 键入'IntrinsicAttributes & { href: string; } & { 孩子们?: 反应节点;颜色?:颜色 |不明确的;禁用?:布尔值 |不明确的; disableElevation?: 布尔值 |不明确的; ... 6 更多 ...;变种?: “文字” | ... 2 更多 ... |不明确的; } & { ...; } & CommonProps<...> & 选择<...>'。类型上不存在属性“fontSize” 'IntrinsicAttributes & { href: string; } & { 孩子?:反应节点; 颜色?:颜色 |不明确的;禁用?:布尔值 |不明确的; disableElevation?: 布尔值 |不明确的; ... 6 更多 ...;变种?: “文字” | ... 2 更多 ... |不明确的; } & { ...; } & CommonProps<...> & 选择<...>'。

重载 2 of 3, '(props: { component: ElementType; } & { 孩子?:反应节点;颜色?:颜色 |不明确的;禁用?:布尔值 | 不明确的; disableElevation?: 布尔值 |不明确的; ... 6 更多 ...; 变种?:“文本”| ... 2 更多 ... |不明确的; } & { ...; } & CommonProps<...> & Pick<...>): Element',出现以下错误。 '{ children: string; 类型中缺少属性 'component'变体: “概述”;颜色:“主要”;字体大小:字符串; }' 但在 类型'{组件:元素类型; }'。

重载 3 of 3, '(props: DefaultComponentProps>>): Element',给出了以下错误。输入'{孩子: 细绳;变体:“概述”;颜色:“主要”;字体大小:字符串; }' 是 不能分配给类型'IntrinsicAttributes & { children?: ReactNode; 颜色?:颜色 |不明确的;禁用?:布尔值 |不明确的; disableElevation?: 布尔值 |不明确的; ... 6 更多 ...;变种?: “文字” | ... 2 更多 ... |不明确的; } & { ...; } & CommonProps<...> & 选择<...>'。类型上不存在属性“fontSize” 'IntrinsicAttributes & { children?: ReactNode;颜色?:颜色 | 不明确的;禁用?:布尔值 |不明确的; disableElevation?: 布尔值 |不明确的; ... 6 更多 ...;变种?:“文本”| ... 2 更多 ... | 不明确的; } & { ...; } & CommonProps<...> & Pick<...>'。

尝试 1: 根据this stack-overflow question 的回答,我尝试重新声明按钮,但它引发了似乎未解决的打字稿错误(https://github.com/Microsoft/TypeScript/issues/17547)。

declare module '@material-ui/core' {
    export interface MyButtonProps {
        fontSize: 'small' | 'medium' | 'large';
    }
    export class Button extends StyledComponent<ButtonProps & MyProps> {
    }
}

尝试2: 尝试覆盖 ButtonTypeMap 而不是 Button,但这也无济于事。

declare module '@material-ui/core/Button' {
    export type ButtonTypeMap<P = {}, D extends React.ElementType<any> = "button"> = ExtendButtonBaseTypeMap<{
        props: P & {
            children?: React.ReactNode;
            color?: CustomColors;
            disabled?: boolean;
            disableElevation?: boolean;
            disableFocusRipple?: boolean;
            endIcon?: React.ReactNode;
            fullWidth?: boolean;
            href?: string;
            size?: 'small' | 'medium' | 'large';
            startIcon?: React.ReactNode;
            variant?: 'text' | 'contained';
            fontSize: 'small' | 'medium' | 'large';
        };
        defaultComponent: D;
        classKey: ButtonClassKey;
    }>
    // The next line throws error with 'Button' is already declared in the upper scope
    // declare const Button: ExtendButtonBase<ButtonTypeMap>;
}

版本:

打字稿:4.2.4

@material-ui/core:4.11.4

编辑:这里有一些答案 (https://stackoverflow.com/a/65344567/2860486),它添加了一个自定义 HOC,它扩展了材料 UI 组件以实现所需的行为,但我想覆盖材料 UI 组件本身,以与从“导入组件”保持一致material-ui" 不是来自我本地的自定义组件文件夹。

【问题讨论】:

    标签: reactjs typescript material-ui


    【解决方案1】:

    配合 Mui5 使用

    import React, { useState } from 'react';
    import { Button as MuiButton, ButtonProps } from '@mui/material';
    import { styled } from '@mui/material/styles';
    
    import { ITheme } from '../../mytheme';
    
    export interface IButton extends Omit<ButtonProps, 'size'> {
      size?: 'small' | 'medium' | 'large' | 'huge';
      theme?: ITheme;
    }
    
    const Button: React.FC<IButton> = styled(MuiButton, {
      skipSx: true,
      skipVariantsResolver: true,
    })<IButton>(({ theme }) => {
      return {//..add your styleoverrides here! or use a theming only approach};
    });
    
    

    主题仅不嵌套MuiComponents跟随https://mui.com/customization/theme-components/#adding-new-component-variants

    declare module '@mui/material/Button' {
      interface ButtonPropsVariantOverrides {
       size: 'large' | 'small' | 'likemoon | 'whatsoever';
      }
    } 
    

    干杯

    【讨论】:

      【解决方案2】:

      您可以简单地使用 ButtonProps 并对其进行扩展并添加您的自定义属性以及扩展所有其余属性。

      自定义按钮组件

      import {
        Button as MuiButton,
        ButtonProps,
        makeStyles
      } from "@material-ui/core";
      
      interface IButtonProps extends ButtonProps {
        fontSize?: "small" | "medium" | "large";
      }
      
      const useStyles = makeStyles({
        small: {
          fontSize: "0.7em"
        },
        medium: {
          fontSize: "1.0em"
        },
        large: {
          fontSize: "1.4em"
        }
      });
      
      function Button({ fontSize = "medium", children, ...rest }: IButtonProps) {
        const classes = useStyles();
        return (
          <MuiButton classes={{ label: classes[fontSize] }} {...rest}>
            {children}
          </MuiButton>
        );
      }
      
      export default Button;
      

      您将拥有 MUI Button 的每个道具以及您的自定义道具(如 fontSize)自动完成:

      <div className="buttons">
        <Button
          fontSize="small"
          variant="contained"
          onClick={() => console.log("small button")}
        >
          Small
        </Button>
        <Button
          fontSize="medium"
          variant="contained"
          onClick={() => console.log("medium button")}
        >
          Medium
        </Button>
        <Button
          fontSize="large"
          variant="contained"
          onClick={() => console.log("large button")}
        >
          Large
        </Button>
      </div>
      

      结果

      您可以在CodeSandBox React TS Demo App 尝试完整代码完成的工作示例。

      PS:如果您使用 ESLint,请不要忘记通过禁用规则 react/jsx-props-no-spreading 来允许 JSX Props Spreading。

      /* eslint-disable react/jsx-props-no-spreading */
      

      【讨论】:

      • 感谢@Christos 的分享。我有一个类似的自定义组件,但想覆盖材料 ui 组件本身来实现它,因为我不想从不同的路径(不是从“@material-ui”而是某个本地文件夹)导入一个特定的组件,因为它会中断一致性,我希望它成为我代码库中的全局变化/主题。我也将它添加到问题中。干杯!
      • 不客气@RahulGandhi。 IMO,您出于不好的理由使事情过于复杂。这就是 React 的工作方式,我们创建组件并覆盖这些组件以创建新组件。即使您设法实现 MUI 模块类型/接口工作,您将如何实现您的fontSize 逻辑?你不会创建一个本地文件来做到这一点吗?此外,这种类型操作根本无助于一致性,相反,它不仅会给将来遇到您的代码的其他开发人员造成混乱和不一致,甚至对您自己也是如此。
      • 感谢您分享您的观点@Christos。我正在为代码库中使用的 material-ui 组件创建一个自定义主题,作为其中的一部分,我希望扩展按钮组件以支持不同的变体。关于 fontSize 逻辑,我将在本地 css 中解决它。我知道这听起来可能有点复杂,但实际上,它实际上是在简化代码库。有一个主题文件夹,可以对 material-ui 组件进行所有必要的自定义。这就是他们很好的建议。 material-ui.com/customization/typography
      猜你喜欢
      • 1970-01-01
      • 2018-02-19
      • 2020-07-22
      • 1970-01-01
      • 2020-07-20
      • 1970-01-01
      • 1970-01-01
      • 2020-08-12
      • 2019-07-15
      相关资源
      最近更新 更多