在 typescript 中扩展 material-ui 组件
Posted
技术标签:
【中文标题】在 typescript 中扩展 material-ui 组件【英文标题】:Extend material-ui component in typescript 【发布时间】:2021-08-10 23:04:45 【问题描述】:尝试扩展 Material-ui Button 组件以添加新的道具。
目的是添加一个新的道具:fontSize
,它有三个选项 - small
、medium
、large
。
<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 中无法访问