TypeScript 2.1 now supports object spread/rest, so no workarounds are needed anymore!
TypeScript s
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;
I've accepted Nitzen Tomer's answer because it was the basic idea I was going for.
As a more generalized solution this is what I ended up going with:
export function rest(object: any, remove: {[key: string]: any}) {
let rest = Object.assign({}, object);
Object.keys(remove).forEach(key => delete rest[key]);
return rest;
}
So I can use it like this:
const {a, b, c} = props;
const htmlProps = rest(props, {a, b, c});
And once TypeScript supports object rest/spread I can just look for all usages of rest()
and simplify it to const {a, b, c, ...htmlProps} = props
.
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.
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" />
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.
use ...rest
type ButtonProps = {
disabled: boolean;
};
function Button(props: ButtonProps): JSX.Element {
const {disabled = false, ...rest} = props;
...
return (
<button disabled={disabled} {...rest}>
....