从 js 重构为 tsx

Posted

技术标签:

【中文标题】从 js 重构为 tsx【英文标题】:Refactoring from js to tsx 【发布时间】:2021-06-17 17:12:54 【问题描述】:

在 React 中我正在尝试将 js 组件重写为 tsx 组件。因此,我只是将 PropTypes 转换为 TypeScript“类型”。刚开始使用 TS,我产生了大量错误“不存在”(ts2339)和“类型缺失”(ts2741)

我的旧 (js) 文件:

    import React from "react";
import PropTypes from "prop-types";

import 
  Dialog,
  DialogTitle,
  DialogContent,
  Typography,
  Tooltip,
  IconButton,
  List,
  ListItem,
  ListItemText,
 from "@material-ui/core";

import  makeStyles  from "@material-ui/core/styles";

import  Close as CloseIcon  from "@material-ui/icons";

const useStyles = makeStyles((theme) => (
  closeButton: 
    position: "absolute",
    right: theme.spacing(1),
    top: theme.spacing(1),
  ,
));

function AboutDialog(props) 
  const classes = useStyles();
  const dialogProps = props.dialogProps;
  const user = props.user;
  const version = process.env.REACT_APP_VERSION;

  if (!user && !version) 
    return null;
  

  return (
    <Dialog fullWidth maxWidth="xs" ...dialogProps>
      <DialogTitle disableTypography>
        <Typography variant="h6">
          About process.env.REACT_APP_TITLE
        </Typography>

        <Tooltip title="Close">
          <IconButton
            className=classes.closeButton
            onClick=dialogProps.onClose
          >
            <CloseIcon />
          </IconButton>
        </Tooltip>
      </DialogTitle>

      <DialogContent>
        <List disablePadding>
          version && (
            <ListItem>
              <ListItemText primary="Version" secondary=version />
            </ListItem>
          )

          user && (
            <ListItem>
              <ListItemText primary="UID" secondary=user.uid />
            </ListItem>
          )
        </List>
      </DialogContent>
    </Dialog>
  );


AboutDialog.propTypes = 
  dialogProps: PropTypes.object.isRequired,
  user: PropTypes.object,
;

export default AboutDialog;

我的新 (tsx) 文件:

import React from "react";

import 
  Dialog,
  DialogTitle,
  DialogContent,
  Typography,
  Tooltip,
  IconButton,
  List,
  ListItem,
  ListItemText,
 from "@material-ui/core";

import  makeStyles  from "@material-ui/core/styles";

import  Close as CloseIcon  from "@material-ui/icons";

const useStyles = makeStyles((theme) => (
  closeButton: 
    position: "absolute",
    right: theme.spacing(1),
    top: theme.spacing(1),
  ,
));

type AboutDialogProps = 
  dialogProps: object;
  user?: object;
;

const AboutDialog: React.FC<AboutDialogProps> = (props) => 
  const classes = useStyles();
  const dialogProps = props.dialogProps;
  const user = props.user;
  const version = process.env.REACT_APP_VERSION;

  if (!user && !version) 
    return null;
  

  return (
    <Dialog fullWidth maxWidth="xs" ...dialogProps>
      <DialogTitle disableTypography>
        <Typography variant="h6">
          About process.env.REACT_APP_TITLE
        </Typography>

        <Tooltip title="Close">
          <IconButton
            className=classes.closeButton
            onClick=dialogProps.onClose
          >
            <CloseIcon />
          </IconButton>
        </Tooltip>
      </DialogTitle>

      <DialogContent>
        <List disablePadding>
          version && (
            <ListItem>
              <ListItemText primary="Version" secondary=version />
            </ListItem>
          )

          user && (
            <ListItem>
              <ListItemText primary="UID" secondary=user.uid />
            </ListItem>
          )
        </List>
      </DialogContent>
    </Dialog>
  );
;

export default AboutDialog;

错误 1: 类型 ' children: Element[]; 中缺少属性 'open';全宽:真;最大宽度:“xs”; ' 但在类型 'DialogProps'.ts(2741) 中是必需的 Dialog.d.ts(88, 3): 'open' 在这里声明。

错误 2: 'object'.ts(2339) 类型上不存在属性 'onClose'

错误 3: 类型 'object'.ts(2339) 上不存在属性 'uid'

试图修复其他组件中的内容只会产生更多错误。我在这里在概念上无法理解什么? // 你将如何转换/重构它?

从前两个 cmets 修复后更新(3 月 21 日): 从“反应”导入反应;

import 
  Dialog,
  DialogTitle,
  DialogContent,
  Typography,
  Tooltip,
  IconButton,
  List,
  ListItem,
  ListItemText,
  DialogProps,
 from "@material-ui/core";

import  makeStyles  from "@material-ui/core/styles";

import  Close as CloseIcon  from "@material-ui/icons";

const useStyles = makeStyles((theme) => (
  closeButton: 
    position: "absolute",
    right: theme.spacing(1),
    top: theme.spacing(1),
  ,
));

interface User 
  uid: string;
  email: string;
  emailVerified: string;
  password: string;
  displayName: string;
  photoURL: string;
  disabled: string;
;

interface AboutDialogProps 
  dialogProps?: DialogProps;
  user?: User;
  onClose: () => void;
;

const AboutDialog = (props: AboutDialogProps) => 
  const classes = useStyles();
  const dialogProps = props.dialogProps;
  const user = props.user;
  const version = process.env.REACT_APP_VERSION;

  if (!user && !version) 
    return null;
  

  return (
    <Dialog fullWidth maxWidth="xs" ...dialogProps>
      <DialogTitle disableTypography>
        <Typography variant="h6">
          About process.env.REACT_APP_TITLE
        </Typography>

        <Tooltip title="Close">
          <IconButton
            className=classes.closeButton
            onClick=dialogProps.onClose
          >
            <CloseIcon />
          </IconButton>
        </Tooltip>
      </DialogTitle>

      <DialogContent>
        <List disablePadding>
          version && (
            <ListItem>
              <ListItemText primary="Version" secondary=version />
            </ListItem>
          )

          user && (
            <ListItem>
              <ListItemText primary="UID" secondary=user.uid />
            </ListItem>
          )
        </List>
      </DialogContent>
    </Dialog>
  );
;

export default AboutDialog;

这会在 IconButton 上引发新错误: " 没有重载匹配此调用。 重载 1 of 3, '(props: href: string; & color?: Color | undefined; disableFocusRipple?: boolean | undefined; edge?: false | "end" | "start" | undefined; size?: " medium" | "small" | undefined; & ...; & CommonProps<...> & Pick<...>): Element',给出了以下错误。 输入'((事件:,原因:“backdropClick”|“escapeKeyDown”)=> void)| undefined' 不可分配给类型 'MouseEventHandler |不明确的'。 类型 '(event: , reason: "backdropClick" | "escapeKeyDown") => void' 不可分配给类型 'MouseEventHandler'。 重载 2 of 3, '(props: component: ElementType; & color?: Color | undefined; disableFocusRipple?: boolean | undefined; edge?: false | "end" | "start" | undefined; size?:" medium" | "small" | undefined; & ...; & CommonProps<...> & Pick<...>): Element',给出了以下错误。 类型“ children: Element;”中缺少属性“组件”;类名:字符串; onClick: ((事件: , 原因: "backdropClick" | "escapeKeyDown") => void) |不明确的; ' 但在类型 ' component: ElementType; '。 重载 3 个,共 3 个,'(props: DefaultComponentProps>>): Element',给出了以下错误。 输入'((事件:,原因:“backdropClick”|“escapeKeyDown”)=> void)| undefined' 不可分配给类型 'MouseEventHandler |不明确的'。 类型'(事件:,原因:“backdropClick”|“escapeKeyDown”)=> void'不可分配给类型'MouseEventHandler'.ts(2769) index.d.ts(1449, 9):预期类型来自属性“onClick”,该属性在此处声明为类型“IntrinsicAttributes & href: string; & 颜色?:颜色 |不明确的; disableFocusRipple?: 布尔值 |不明确的;边缘?:假| “结束” | “开始” |不明确的;尺寸?:“中”| ... 1 更多 ... |不明确的; & ...; & CommonProps<...> & Pick<...>' OverridableComponent.d.ts(17, 7): 'component' 在这里声明。 index.d.ts(1449, 9):预期类型来自属性“onClick”,该属性在此处声明为类型“IntrinsicAttributes & color?: Color |不明确的; disableFocusRipple?: 布尔值 |不明确的;边缘?:假| “结束” | “开始” |不明确的;尺寸?:“中”| “小” |不明确的; & ...; & CommonProps<...> & Pick<...>' "

... 并在对话框上: " 类型'孩子:元素[]; 'aria- describeby'?: 字符串 |不明确的; 'aria-labelledby'?: 字符串 |不明确的; disableBackdropClick?: 布尔值 |不明确的; disableEscapeKeyDown?: 布尔值 |不明确的; ... 285 更多 ...;类?:部分<...> |不明确的; ' 不可分配给类型 'DialogProps'。 属性“开放”的类型不兼容。 输入'布尔| undefined' 不可分配给类型 'boolean'。 类型“未定义”不可分配给类型“布尔”。ts(2322) "

【问题讨论】:

此外,可用于将 js 转换为 typescript 的 npm 包似乎下载量很少(例如 react-proptypes-to-typescript 或 react-js-to-ts)。我想很多人在将 js 转换为 ts 时一定有类似的问题,除了自动化之外,你能推荐什么吗? 【参考方案1】:

Typescript 是强类型语言。 让我们关注错误:

    open 是未键入的。 onClose 没有类型。 uid 没有类型。

它是对象,所以我建议创建接口 IOpen:

interface IOpen 
 children: Array<Element>
 fullWidth: boolean;
 maxWidth: string

onClose - 可能需要输入:

onClose: () => void

uid - 可能需要类型 - 我看到 uid 是用户 object 内部的 string。所以有可能添加另一个接口:

interface IUser 
 (...)
 uid:string
 (...)

在您的情况下,您键入 user like object 是真的 - 但 ts 编译器需要更具体的信息。

【讨论】:

谢谢,我不确定我是否理解 100%。我编辑了上面的文件。 onClose 类型是否正确实现?然后应该从 material-ui 导入接口 IOpen(如上面编辑的解决方案中所做的那样)? 如果它是库的一部分,那么它应该包含它的类型 哈哈 - 欢迎来到打字稿的阴暗面 刚刚通过 materialUI 文档再次实现 TS。它变得更加痛苦:material-ui.com/guides/typescript :) 仍然无法正常工作我认为最好的方法是保留现有的 js(x) 文件并仅将 TS 添加到新组件中......【参考方案2】:

在定义类型时,您希望比“对象”更具体一点。 像这样的东西应该可以工作

type User = 
    uid: string;
    //add all the property of your user
;

type AboutDialogProps = 
    dialogProps: DialogProps; //DialogProps is imported from @material-ui/core
    user?: User;
;

【讨论】:

谢谢,这修复了它的一部分,但也产生了一些新的错误。你们一般是怎么做的。这是要重构为 TS 的 25 个文件中的第一个,它需要花费几个小时来阅读错误。我可以将现有文件保留在 JS 中并在新文件中使用 TS 构建吗?如果它们使用旧 JS 文件的一部分并且一切都应该顺利运行,这是否意味着在新文件中简单地定义接口/类型?

以上是关于从 js 重构为 tsx的主要内容,如果未能解决你的问题,请参考以下文章

将 d3 js 代码从 v5.16 重构到 v6.6.2,d3.event 重大更改

如何将前端 JS 重构为 Angular 2 以与 PHP MVC 后端完美配合? [关闭]

如何将使用 fs.readFileSync() 的 Node.js 代码重构为使用 fs.readFile()?

如何做一个好的前端重构工程师

Ajax 重构的步骤

重构:从Promise到Async/Await