我想用自己的React功能组件封装一个标准的按钮,但我希望新组件的用户能够设置几乎所有底层按钮的道具。当然,我希望保持正确的输入方式,所以如果WrappedButton封装了一个 button
那么
<WrappedButton formNoValidate={true} onClick={handleClick} />
会正确编译,但
<WrappedButton formNoValidate={5} onKrapnik={handleClick} />
不会,既因为 formNoValidate
类型为布尔型,并且 onKrapnik
不存在。
很明显,我不想把底层组件的合法道具做一个详尽的(且脆弱的)列表。
原组件有一些道具。 对于每个道具,你可能要
你也可能想添加一些自己的新道具。我们举个简单的例子,你只是想添加一些新的道具。我正在做一种新的按钮,叫做 MultiClick
,它增加了一个可选的道具。
type MultiClickProps = {
clickCount?: number;
} & JSX.IntrinsicElements["button"];
const MultiClick: React.FC<MultiClickProps> = ({
clickCount = 2,
...buttonProps
}) => {
// do some stuff
return (
<button {...buttonProps} />
);
};
当然,这并没有什么用处:如果最终结果是返回一个所有道具都未修改的按钮,那么 "一些东西 "怎么会有意义呢?
所以,你可能要拦截并修改一些道具。
const MultiClick: React.FC<MultiClickProps> = ({
clickCount = 2,
onClick,
...buttonProps
}) => {
const handleClick = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
// do some stuff
onClick(event);
};
return (
<button {...buttonProps} onClick={handleClick}/>
);
};
这样一来,就有机会与封装组件的动作进行实际交互。
现在假设你不喜欢onClick的 "event "参数,想用别的东西代替,一个数字。 要改变一个类型,实际上你必须省略旧的道具并重新编写它,就像这样。
type MultiClickProps = {
clickCount?: number;
onClick: (n: number) => void;
} & Omit<JSX.IntrinsicElements["button"], 'onClick'>;
你可以看到完成的代码 此处.
顺便说一下,所有这些都取决于JSX.IntrinsicElements的存在,React的人很好心地提供了它。 假设你正试图从某个库中封装一个功能组件,而这个组件是由 不 输出一个整洁的道具类型?
幸运的是,你可以为任何用Typescript编写的功能组件生成你自己的道具类型,用这个。
type PropsOf<T> =
T extends React.FunctionComponent<infer U> ? U : never;
如果应用于上面的函数,就像这样:
type MultiClickProps = PropsOf<typeof MultiClick>;
就会生成这样的结果
type MultiClickProps = {
clickCount?: number;
} & React.ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement>