在打字稿中扩展 material-ui 组件

Rah*_*dhi 7 typescript reactjs material-ui

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

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

<Button variant="outlined" color="primary" fontSize="small">
    button_small
</Button>
Run Code Online (Sandbox Code Playgroud)

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

根据typescript 主题定制的材料 ui 文档,我已经定制了主题并且工作正常。

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

我收到此错误是因为没有重载匹配,这很明显,因为 Material ui Button 组件不知道“fontSize”新道具。

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

重载 1 of 3, '(props: { href: string; } & { children?: ReactNode; color?: Color | undefined; disabled?: boolean | undefined; disableElevation?: boolean | undefined; disableFocusRipple?: boolean | undefined; ... 5 更多...; 变体?:“文本” | ... 2 更多... | 未定义; } & { ...; } & CommonProps<...> & Pick<...>) : 元素',出现以下错误。输入'{孩子:字符串;变体:“概述”;颜色:“原色”;字体大小:字符串;}' 不可分配给类型 'IntrinsicAttributes & { href: string; } & { 孩子?:ReactNode; 颜色?:颜色 | 不明确的; 禁用?:布尔值 | 不明确的; disableElevation?: boolean | 不明确的; ……还有6个……;变体?:“文本” | ... 2 更多... | 不明确的; } & { ...; } & CommonProps<...> & Pick<...> '。属性 'fontSize' 不存在于类型 'IntrinsicAttributes & { href: string; } & { 孩子?:ReactNode; 颜色?:颜色 | 不明确的; 禁用?:布尔值 | 不明确的; disableElevation?: boolean | 不明确的; ……还有6个……;变体?:“文本” | ... 2 更多... | 不明确的; } & { ...; } & CommonProps<...> & Pick<...>'。

重载 2 of 3, '(props: { component: ElementType; } & { children?: ReactNode; color?: Color | undefined; disabled?: boolean | undefined; disableElevation?: boolean | undefined; ... 6 more .. .; 变体?: "text" | ... 2 more ... | undefined; } & { ...; } & CommonProps<...> & Pick<...>): Element', 给出以下内容错误。类型“{children: string;”中缺少属性“component” 变体:“概述”;颜色:“原色”;字体大小:字符串;}' 但在类型 '{ component: ElementType; 中是必需的 }'。

重载 3 of 3, '(props: DefaultComponentProps<ExtendButtonBaseTypeMap<ButtonTypeMap<{}, "button">>>): Element',出现以下错误。输入'{孩子:字符串;变体:“概述”;颜色:“原色”;字体大小:字符串;}' 不可分配到类型 'IntrinsicAttributes & { children?: ReactNode; 颜色?:颜色 | 不明确的; 禁用?:布尔值 | 不明确的; disableElevation?: boolean | 不明确的; ……还有6个……;变体?:“文本” | ... 2 更多... | 不明确的; } & { ...; } & CommonProps<...> & Pick<...>'。属性 'fontSize' 不存在于类型 'IntrinsicAttributes & { children?: ReactNode; 颜色?:颜色 | 不明确的; 禁用?:布尔值 | 不明确的; disableElevation?: boolean | 不明确的; ……还有6个……;变种?:“

尝试 1:根据此堆栈溢出问题的答案, 我尝试重新声明 Button,但它引发了一个似乎未解决的打字稿错误 ( 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> {
    }
}
Run Code Online (Sandbox Code Playgroud)

Attempt2:试图覆盖 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>;
}
Run Code Online (Sandbox Code Playgroud)

版本:

打字稿: 4.2.4

@material-ui/核心: 4.11.4

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

Chr*_*ras 6

您可以简单地使用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;
Run Code Online (Sandbox Code Playgroud)

您将拥有 MUI 按钮的每个道具以及自定义道具,以自动完成,例如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>
Run Code Online (Sandbox Code Playgroud)

结果

自定义道具 MUI 按钮

您可以在此CodeSandBox React TS 演示应用程序中尝试具有完整代码完成的工作示例。

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

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;
Run Code Online (Sandbox Code Playgroud)