React中的休息道具的TypeScript解决方法

Aar*_*all 18 typescript reactjs

针对TypeScript 2.1进行了更新

TypeScript 2.1现在支持对象传播/休息,因此不再需要解决方法!


原始问题

TypeScript支持JSX扩展属性,这些属性在React中通常用于将HTML属性从组件传递到呈现的HTML元素:

interface LinkProps extends React.HTMLAttributes {
  textToDisplay: string;
}

class Link extends React.Component<LinkProps, {}> {
  public render():JSX.Element {
    return (
      <a {...this.props}>{this.props.textToDisplay}</a>
    );
  }
}

<Link textToDisplay="Search" href="http://google.com" />
Run Code Online (Sandbox Code Playgroud)

但是,如果将任何未知道具传递给HTML元素,React会引入警告.上面的例子会产生一个React运行时警告,它textToDisplay是一个未知的支柱<a>.针对此示例的案例的建议解决方案是使用对象rest属性来提取自定义道具并将其余部分用于JSX spread属性:

const {textToDisplay, ...htmlProps} = this.props;
return (
  <a {...htmlProps}>{textToDisplay}</a>
);
Run Code Online (Sandbox Code Playgroud)

但是TypeScript还不支持这种语法.我知道希望有一天我们能够在TypeScript中做到这一点.(更新:TS 2.1现在支持对象传播/休息!为什么你还在读这个?)同时有什么解决方法?我正在寻找一种不会影响类型安全性并且发现它非常困难的解决方案.例如,我可以这样做:

const customProps = ["textDoDisplay", "otherCustomProp", "etc"];
const htmlProps:HTMLAttributes = Object.assign({}, this.props);
customProps.forEach(prop => delete htmlProps[prop]);
Run Code Online (Sandbox Code Playgroud)

但是这需要使用未针对实际道具验证的字符串属性名称,因此容易出现拼写错误和错误的IDE支持.有没有更好的方法可以做到这一点?

Wil*_*lly 76

It's actually easier than all of the answers above. You just need to follow the example below:

type Props = {
  id: number,
  name: string;
  // All other props
  [x:string]: any;
}

const MyComponent:React.FC<Props> = props => {
  // Any property passed to the component will be accessible here
}
Run Code Online (Sandbox Code Playgroud)

Hope this helps.

  • 呵呵。我原来的问题一定措辞不好,因为这个答案根本没有解决这个问题,但却得到了最多的支持。:-P 我猜很多人都在谷歌搜索“rest props”,试图找出如何用任意 props 重载类型,实际上这里的宽容索引类型就是这样做的。我的问题是关于 ES6 spread/rest 语法和类型安全。 (4认同)

ade*_*hox 12

对于那些可能无法很快理解[x:string]: any;接受的答案中的内容的人:虽然它很像数组的语法,但它确实指定了一个对象,其键的类型为string,其值的类型为any。在 TypeScript 的术语中,它被称为“索引签名”。

但是,还要注意,有时,作为替代且类型不那么宽松的解决方案,您正在使用的库也可能具有导出的类型,因此您可以使用这些类型。

例如,当扩展 Ant 的 Button 时,可以这样做:

import { ReactNode } from "react";
import { Button as AntButton } from "antd";
import { NativeButtonProps } from "antd/lib/button/button";

interface IButtonProps {
  children?: ReactNode;
}

const Button = ({
  children,
  ...rest
}: IButtonProps & NativeButtonProps): JSX.Element => {
  return <AntButton {...rest}>{children}</AntButton>;
};

export default Button;
Run Code Online (Sandbox Code Playgroud)

注意 1: TypeScript 中的与号 (&) 运算符IButtonProps & NativeButtonProps只是对类型进行“合并”。现在,您不会失去自己 Button 上 Ant Button 道具的智能感知,因为您不再使用any。Ant Button 的类型和 IButtonProps 是组合在一起的,因此两者都存在。

注意2:你可能想知道我在哪里找到这种类型的。这种类型在这里导出: https: //github.com/ant-design/ant-design/blob/master/components/button/button.tsx#L124 而且它的包含路径可以使用智能感知来实现,只需开始输入 NativeButton ...并且必须向您建议。


Dam*_*een 6

上面例子中的 React.HtmlAttributes 现在是通用的,所以我需要从React.AnchorHTMLAttributes<HTMLAnchorElement>.

例子:

import React from 'react';

type  AClickEvent = React.MouseEvent<HTMLAnchorElement>;

interface LinkPropTypes extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
    to: string;
    onClick?: (x: AClickEvent) => void;
}

class Link extends React.Component<LinkPropTypes> {
  public static defaultProps: LinkPropTypes = {
    to: '',
    onClick: null,
  };

private handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
   ...
    event.preventDefault();
    history.push(this.props.to);
 };

  public render() {
    const { to, children, ...props } = this.props;
    return (
      <a href={to} {...props} onClick={this.handleClick}>
        {children}
      </a>
    );
    }
}

export default Link;
Run Code Online (Sandbox Code Playgroud)


Met*_*lik 6

...rest

type ButtonProps = {
    disabled: boolean;
};

function Button(props: ButtonProps): JSX.Element {
    const {disabled = false, ...rest} = props;
...
return (
    <button disabled={disabled} {...rest}>
....
Run Code Online (Sandbox Code Playgroud)

  • 但是你如何真正推断出传递给“button”的内容实际上是一个有效的 HTML prop 或事件? (3认同)

Nit*_*mer 5

您可能无法避免使用的属性的子集创建新对象this.props,但是可以使用类型安全性来做到这一点。

例如:

interface LinkProps {
    textToDisplay: string;
}

const LinkPropsKeys: LinkProps = { textToDisplay: "" };

class Link extends React.Component<LinkProps & React.HTMLAttributes, {}> {
    public render(): JSX.Element {
        return (
            <a { ...this.getHtmlProps() }>{ this.props.textToDisplay }</a>
        );
    }

    private getHtmlProps(): React.HTMLAttributes {
        let htmlProps = {} as React.HTMLAttributes;

        for (let key in this.props) {
            if (!(LinkPropsKeys as any)[key]) {
                htmlProps[key] = this.props[key];
            }
        }

        return htmlProps;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用LinkPropsKeys需要与匹配的object LinkProps可以帮助您使接口和运行时查找之间的键保持同步。


Okk*_*kku 5

像这样的吸气剂可以工作:

class Link extends React.Component<{
  textToDisplay: string;
} & React.HTMLAttributes<HTMLDivElement>> {

  static propTypes = {
    textToDisplay: PropTypes.string;
  }

  private get HtmlProps(): React.HTMLAttributes<HTMLAnchorElement> {
    return Object.fromEntries(
      Object.entries(this.props)
      .filter(([key]) => !Object.keys(Link.propTypes).includes(key))
    );
  }

  public render():JSX.Element {
    return (
      <a {...this.HtmlProps}>
        {this.props.textToDisplay}
      </a>
    );
  }
}

<Link textToDisplay="Search" href="http://google.com" />
Run Code Online (Sandbox Code Playgroud)