TypeScript workaround for rest props in React

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
}

Hope this helps.


You probably can't avoid creating a new object with a subset of the properties of this.props, but you can do that with type safety.

For example:

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;
    }
}

Using LinkPropsKeys object, which needs to match the LinkProps, will help you keep the keys between the interface and the runtime lookup synchronized.


React.HtmlAttributes in the example above is now generic so I needed to extend from React.AnchorHTMLAttributes<HTMLAnchorElement>.

Example:

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;

use ...rest

type ButtonProps = {
    disabled: boolean;
};

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

A getter like this could work:

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" />