React 中休息道具的 TypeScript 解决方法
Posted
技术标签:
【中文标题】React 中休息道具的 TypeScript 解决方法【英文标题】:TypeScript workaround for rest props in React 【发布时间】:2017-02-23 06:17:07 【问题描述】:针对 TypeScript 2.1 更新
TypeScript 2.1 now supports object spread/rest,因此不再需要解决方法!
原始问题
TypeScript 支持 JSX spread attributes,它在 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" />
然而,React 引入了warning if you pass any unknown props to an HTML element。上面的例子会产生一个 React 运行时警告 textToDisplay
是 <a>
的一个未知属性。对于像这个例子这样的案例,建议的解决方案是使用object rest properties 提取您的自定义道具并将其余部分用于 JSX 传播属性:
const textToDisplay, ...htmlProps = this.props;
return (
<a ...htmlProps>textToDisplay</a>
);
但是 TypeScript 还不支持这种语法。我知道希望有一天we will be able to do this in TypeScript。(更新:TS 2.1 now supports object spread/rest!你为什么还在读这个??)解决方法?我正在寻找一种不会影响类型安全的解决方案,并且发现它非常困难。例如我可以这样做:
const customProps = ["textDoDisplay", "otherCustomProp", "etc"];
const htmlProps:HTMLAttributes = Object.assign(, this.props);
customProps.forEach(prop => delete htmlProps[prop]);
但这需要使用未针对实际道具进行验证的字符串属性名称,因此容易出现拼写错误和不良的 IDE 支持。有没有更好的方法可以做到这一点?
【问题讨论】:
请注意,您正在寻找的语法现在可用 @KyleGobel 真的,我很高兴。 :) 这个问题应该被删除吗? “TypeScript 2.1 现在支持对象传播/休息,因此不再需要变通方法!”你怎么用这个?!! @gyozokudor 只需使用 TypeScript 2.1 或更高版本(现在最高 3.4!)并使用原始示例:const textToDisplay, ...htmlProps = this.props
【参考方案1】:
这实际上比上面所有的答案都容易。您只需要按照以下示例进行操作:
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
希望这会有所帮助。
【讨论】:
谢谢,但我认为您误解了错误。这不是 TypeScript 编译错误,而是由于将(通过 JSX 扩展语法)属性传递给无法识别的 HTML 元素而导致的 React 运行时错误,例如上面示例中<a>
标记上的 textToDisplay
。无论如何,TS 2.1 早就解决了这个问题,增加了对对象休息/扩展语法的支持。
这正是我想要的!谢谢
完美。谢谢!!【参考方案2】:
您可能无法避免使用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;
使用需要匹配LinkProps
的LinkPropsKeys
对象将帮助您保持界面和运行时查找之间的键同步。
【讨论】:
很好的建议,这很有帮助。【参考方案3】:使用...rest
type ButtonProps =
disabled: boolean;
;
function Button(props: ButtonProps): JSX.Element
const disabled = false, ...rest = props;
...
return (
<button disabled=disabled ...rest>
....
【讨论】:
但是您如何真正推断传递给button
的内容实际上是有效的HTML 道具或事件?【参考方案4】:
上面示例中的 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;
【讨论】:
【参考方案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" />
【讨论】:
【参考方案6】:我接受了 Nitzen Tomer 的回答,因为这是我所追求的基本理念。
作为一个更通用的解决方案,这是我最终采用的解决方案:
export function rest(object: any, remove: [key: string]: any)
let rest = Object.assign(, object);
Object.keys(remove).forEach(key => delete rest[key]);
return rest;
所以我可以这样使用它:
const a, b, c = props;
const htmlProps = rest(props, a, b, c);
一旦 TypeScript 支持 object rest/spread,我就可以查找 rest()
的所有用法并将其简化为 const a, b, c, ...htmlProps = props
。
【讨论】:
【参考方案7】:如果您将 ...rest 作为参数传递给组件,TypeScript 现在会忽略它。在我看来 ...rest 参数不需要类型安全,因为这些是由父组件传递给子组件的默认参数。例如 redux 将有关 store 的信息传递给子组件,因此 ...rest 参数始终存在并且不需要类型安全或 propTypes。
//here's the right solution
interface schema
loading: boolean
//pass ...rest as argument
export function MyComponent(loading, ...rest:schema)
if (loading) return <h2>Loading ...</h2>
return (
<div ...rest>
<h2>Hello World </h2>
</div>
强文本
【讨论】:
以上是关于React 中休息道具的 TypeScript 解决方法的主要内容,如果未能解决你的问题,请参考以下文章
在没有道具的 Typescript 中使用 React.forwardRef
Eslint:功能组件中的默认道具问题(Typescript - React)
TypeScript:为样式化的组件元素扩展 React 组件道具