在 typescript 中扩展 material-ui 组件

Posted

技术标签:

【中文标题】在 typescript 中扩展 material-ui 组件【英文标题】:Extend material-ui component in typescript 【发布时间】: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://***.com/a/65344567/2860486),它添加了一个自定义 HOC,它扩展了材料 UI 组件以实现所需的行为,但我想覆盖材料 UI 组件本身,以与从“导入组件”保持一致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

以上是关于在 typescript 中扩展 material-ui 组件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Typescript 扩展 Material-UI 组件的道具?

反应如何使用 TypeScript 在 Textfield Material-UI 中使用图标

如何使用 Typescript 为 React 设置 Material-UI?

如何在 typescript 环境中的 material-ui 按钮内渲染 react-router-dom NavLink 的 activeClassName 样式?

Material-UI Typescript 如何在组件类中从 makeStyle() 创建样式实例

Angular-Material 、 AngularJS 、 TypeScript 、 Javascript 、 ng-controller 在我的 md-dialog 中无法访问