Issue
Updated for TypeScript 2.1
TypeScript 2.1 now supports object spread/rest, so no workarounds are needed anymore!
Original Question
TypeScript supports JSX spread attributes which is commonly used in React to pass HTML attributes from a component to a rendered HTML element:
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" />
However, React introduced a warning if you pass any unknown props to an HTML element. The above example would produce a React runtime warning that textToDisplay is an unknown prop of <a>. The suggested solution for a case like this example is to use object rest properties to extract out your custom props and use the rest for the JSX spread attributes:
const {textToDisplay, ...htmlProps} = this.props;
return (
<a {...htmlProps}>{textToDisplay}</a>
);
But TypeScript does not yet support this syntax. I know that hopefully some day we will be able to do this in TypeScript. (Update: TS 2.1 now supports object spread/rest! Why are you still reading this??) In the meantime what are some workarounds? I'm looking for a solution that doesn't compromise type-safety and finding it surprisingly difficult. For example I could do this:
const customProps = ["textDoDisplay", "otherCustomProp", "etc"];
const htmlProps:HTMLAttributes = Object.assign({}, this.props);
customProps.forEach(prop => delete htmlProps[prop]);
But this requires the use of string property names that are not validated against the actual props and thus prone to typos and bad IDE support. Is there a better way we can do this?
Solution
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.
Answered By - Nitzan Tomer
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.