React 中休息道具的 TypeScript 解决方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40032592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
TypeScript workaround for rest props in React
提问by Aaron Beall
Updated for TypeScript 2.1
针对 TypeScript 2.1 更新
TypeScript 2.1 now supports object spread/rest, so no workarounds are needed anymore!
TypeScript 2.1 现在支持 object spread/rest,因此不再需要解决方法!
Original Question
原始问题
TypeScript supports JSX spread attributeswhich is commonly used in React to pass HTML attributes from a component to a rendered HTML element:
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" />
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 propertiesto extract out your custom props and use the rest for the JSX spread attributes:
但是,如果您将任何未知的 props 传递给 HTML 元素,React 会引入警告。上面的例子会产生一个 React 运行时警告,它textToDisplay
是<a>
. 对于像这个例子这样的情况,建议的解决方案是使用对象的其余属性来提取您的自定义道具,并将其余的用于 JSX 传播属性:
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:
但是 TypeScript 还不支持这种语法。我知道希望有一天我们能够在 TypeScript 中做到这一点。(更新:TS 2.1 现在支持对象传播/休息!你为什么还在读这个??)同时有哪些解决方法?我正在寻找一种不会损害类型安全性并且发现它非常困难的解决方案。例如我可以这样做:
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?
但这需要使用未针对实际道具进行验证的字符串属性名称,因此容易出现拼写错误和糟糕的 IDE 支持。有没有更好的方法可以做到这一点?
采纳答案by Nitzan Tomer
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.
您可能无法避免使用 的属性子集创建一个新对象this.props
,但您可以通过类型安全来做到这一点。
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.
使用LinkPropsKeys
需要匹配 的对象LinkProps
将帮助您保持接口和运行时查找之间的键同步。
回答by Willy
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.
希望这可以帮助。
回答by Damian Green
React.HtmlAttributes in the example above is now generic so I needed to extend from React.AnchorHTMLAttributes<HTMLAnchorElement>
.
上面例子中的 React.HtmlAttributes 现在是通用的,所以我需要从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;
回答by Aaron Beall
I've accepted Nitzen Tomer's answer because it was the basic idea I was going for.
我已经接受了 Nitzen Tomer 的回答,因为这是我想要的基本想法。
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
.
一旦 TypeScript 支持对象静止/传播,我就可以查找所有的用法rest()
并将其简化为const {a, b, c, ...htmlProps} = props
.
回答by Okku
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" />