首页 > 解决方案 > TypeScript 中的 Material UI 实现

问题描述

我是 typeScript 的新手,我正在使用这个 materialUI 高级主题 OnePirate,我正在使用 Footer 组件,它在 js 中工作得非常好,但是当我将同一个 Footer 组件移动到我的项目并将组件重命名为 .tsx 它时抛出错误说“没有重载匹配这个调用”这是代码:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Link from "@material-ui/core/Link";
import Container from "@material-ui/core/Container";
import Typography from "./materialUi/Typography";
import TextField from "./materialUi/TextField";
import fbicon from "./static/themes/onepirate/appFooterFacebook.png";
import twicon from "./static/themes/onepirate/appFooterTwitter.png";

const useStyles = makeStyles((theme) => ({
  root: {
    display: "flex",
    backgroundColor: theme.palette.secondary.light,
  },
  container: {
    marginTop: theme.spacing(8),
    marginBottom: theme.spacing(8),
    display: "flex",
  },
  iconsWrapper: {
    height: 120,
  },
  icons: {
    display: "flex",
  },
  icon: {
    width: 48,
    height: 48,
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: theme.palette.warning.main,
    marginRight: theme.spacing(1),
    "&:hover": {
      backgroundColor: theme.palette.warning.dark,
    },
  },
  list: {
    margin: 0,
    listStyle: "none",
    paddingLeft: 0,
  },
  listItem: {
    paddingTop: theme.spacing(0.5),
    paddingBottom: theme.spacing(0.5),
  },
  language: {
    marginTop: theme.spacing(1),
    width: 150,
  },
}));

const LANGUAGES = [
  {
    code: "en-US",
    name: "English",
  },
  {
    code: "fr-FR",
    name: "Français",
  },
];

function Footer() {
  const classes = useStyles();

  return (
    <Typography component="footer" className={classes.root}>
      <Container className={classes.container}>
        <Grid container spacing={5}>
          <Grid item xs={6} sm={4} md={3}>
            <Grid
              container
              direction="column"
              justify="flex-end"
              className={classes.iconsWrapper}
              spacing={2}
            >
              <Grid item className={classes.icons}>
                <a href="https://material-ui.com/" className={classes.icon}>
                  <img src={fbicon} alt="Facebook" />
                </a>
                <a
                  href="https://twitter.com/MaterialUI"
                  className={classes.icon}
                >
                  <img src={twicon} alt="Twitter" />
                </a>
              </Grid>
              <Grid item>© 2019 Onepirate</Grid>
            </Grid>
          </Grid>
          <Grid item xs={6} sm={4} md={2}>
            <Typography variant="h6" marked="left" gutterBottom>
              Legal
            </Typography>
            <ul className={classes.list}>
              <li className={classes.listItem}>
                <Link href="/premium-themes/onepirate/terms/">Terms</Link>
              </li>
              <li className={classes.listItem}>
                <Link href="/premium-themes/onepirate/privacy/">Privacy</Link>
              </li>
            </ul>
          </Grid>
          <Grid item xs={6} sm={8} md={4}>
            <Typography variant="h6" marked="left" gutterBottom>
              Language
            </Typography>
            <TextField
              select
              SelectProps={{
                native: true,
              }}
              className={classes.language}
            >
              {LANGUAGES.map((language) => (
                <option value={language.code} key={language.code}>
                  {language.name}
                </option>
              ))}
            </TextField>
          </Grid>
          <Grid item>
            <Typography variant="caption">
              {"Icons made by "}
              <Link
                href="https://www.freepik.com"
                rel="nofollow"
                title="Freepik"
              >
                Freepik
              </Link>
              {" from "}
              <Link
                href="https://www.flaticon.com"
                rel="nofollow"
                title="Flaticon"
              >
                www.flaticon.com
              </Link>
              {" is licensed by "}
              <Link
                href="https://creativecommons.org/licenses/by/3.0/"
                title="Creative Commons BY 3.0"
                target="_blank"
                rel="noopener noreferrer"
              >
                CC 3.0 BY
              </Link>
            </Typography>
          </Grid>
        </Grid>
      </Container>
    </Typography>
  );
}
export default Footer;

如果我不从材质 UI 导入,这是排版代码

import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import { capitalize } from "@material-ui/core/utils";
import MuiTypography from "@material-ui/core/Typography";

const styles = (theme) => ({
  markedH2Center: {
    height: 4,
    width: 73,
    display: "block",
    margin: `${theme.spacing(1)}px auto 0`,
    backgroundColor: theme.palette.secondary.main,
  },
  markedH3Center: {
    height: 4,
    width: 55,
    display: "block",
    margin: `${theme.spacing(1)}px auto 0`,
    backgroundColor: theme.palette.secondary.main,
  },
  markedH4Center: {
    height: 4,
    width: 55,
    display: "block",
    margin: `${theme.spacing(1)}px auto 0`,
    backgroundColor: theme.palette.secondary.main,
  },
  markedH6Left: {
    height: 2,
    width: 28,
    display: "block",
    marginTop: theme.spacing(0.5),
    background: "currentColor",
  },
});

const variantMapping = {
  h1: "h1",
  h2: "h1",
  h3: "h1",
  h4: "h1",
  h5: "h3",
  h6: "h2",
  subtitle1: "h3",
};

function Typography(props) {
  const { children, classes, marked = false, variant, ...other } = props;

  return (
    <MuiTypography variantMapping={variantMapping} variant={variant} {...other}>
      {children}
      {marked ? (
        <span
          className={
            classes[`marked${capitalize(variant) + capitalize(marked)}`]
          }
        />
      ) : null}
    </MuiTypography>
  );
}

Typography.propTypes = {
  children: PropTypes.node,
  classes: PropTypes.object.isRequired,
  marked: PropTypes.oneOf([false, "center", "left"]),
  variant: PropTypes.string,
};

export default withStyles(styles)(Typography);

这是现在的错误:如果我使用排版文件,这是错误

标签: reactjstypescript

解决方案


TL;DR:我已经在 Typescript 中开源了 onepirate 的实现,所以你可以使用它:https ://github.com/rothbart/onepirate-typescript

这是一个很好的问题。Onepirate 是一个很好的资源,但正如您所注意到的,它并不是真正以对 Typescript 友好的方式构建的。

您的问题是 onepirate 中的 Typography 实现在指定它期望的属性方面做得并不好,并且依赖于 vanilla JS 主要将所有内容传递给底层 MuiTypography 组件的事实。

这是一个更简洁的 Typography 实现,它保留了 onepirate 与 Typescript 一起使用的下划线效果:

import React from "react";
import PropTypes, { InferProps } from "prop-types";
import {
  withStyles,
  WithStyles,
  createStyles,
  Theme,
} from "@material-ui/core/styles";
import MuiTypography, { TypographyProps } from "@material-ui/core/Typography";

const styles = (theme: Theme) =>
  createStyles({
    markedH2Center: {
      height: 4,
      width: 73,
      display: "block",
      margin: `${theme.spacing(1)}px auto 0`,
      backgroundColor: theme.palette.secondary.main,
    },
    markedH3Center: {
      height: 4,
      width: 55,
      display: "block",
      margin: `${theme.spacing(1)}px auto 0`,
      backgroundColor: theme.palette.secondary.main,
    },
    markedH4Center: {
      height: 4,
      width: 55,
      display: "block",
      margin: `${theme.spacing(1)}px auto 0`,
      backgroundColor: theme.palette.secondary.main,
    },
    markedH6Left: {
      height: 2,
      width: 28,
      display: "block",
      marginTop: theme.spacing(0.5),
      background: "currentColor",
    },
  });

function getMarkedClassName(variant: string) {
  switch (variant) {
    case "h2":
      return "markedH2Center";
    case "h3":
      return "markedH3Center";
    case "h4":
      return "markedH4Center";
  }
  return "markedH6Left";
}

const variantMapping = {
  h1: "h1",
  h2: "h1",
  h3: "h1",
  h4: "h1",
  h5: "h3",
  h6: "h2",
  subtitle1: "h3",
};

function Typography<C extends React.ElementType>(
  props: TypographyProps<C, { component?: C }> &
    WithStyles<typeof styles> &
    InferProps<typeof Typography.propTypes>
) {
  const { children, variant, classes, marked, ...other } = props;

  return (
    <MuiTypography variantMapping={variantMapping} variant={variant} {...other}>
      {children}
      {marked ? (
        <span className={classes[getMarkedClassName(variant as string)]} />
      ) : null}
    </MuiTypography>
  );
}

Typography.propTypes = {
  marked: PropTypes.oneOf([false, "center", "left"]),
};

Typography.defaultProps = {
  marked: false,
};

export default withStyles(styles)(Typography);

只是提醒一下,其他 onepirate 组件也会遇到同样的问题(我刚刚在 Typescript 中重新实现了其中的大部分,所以我可以在我自己的 Web 应用程序中使用它们)。如果你想逆向工程我是如何做到这一点的,我会特别注意我如何利用 MuiTypography 道具,然后添加我需要的一个新属性(标记)。

TypographyProps<C, { component?: C }> &
    WithStyles<typeof styles> &
    InferProps<typeof Typography.propTypes>

如果您还没有阅读过https://material-ui.com/guides/typescript/ ,我也强烈建议您阅读它,它对于理解 Material-UI 中的一些 Typescript 问题非常有用。


推荐阅读