当组件是通用的(多态反应组件)时键入点击处理程序



这是我的组件

interface Props<C extends React.ElementType> {
as?: C;
children: React.ReactNode;
size?: "mini" | "small" | "medium" | "large";
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
}
type ButtonProps<C extends React.ElementType> = Props<C> &
Omit<React.ComponentPropsWithRef<C>, keyof Props<C>>;
export const Button = <C extends React.ElementType = "button">({
children,
as,
size = "medium",
onClick,
...restProps
}: ButtonProps<C>) => {
const Component = as || "button";
return (
<Component {...restProps} className={`button-${size}`} onClick={onClick}>
{children}
</Component>
);
};

我可以这样使用组件:

<Button as="a" onClick={(e) => console.log(e)}>Anchor button</a>

问题是,click处理程序的类型说事件的类型是React.MouseEvent<HTMLButtonElement, MouseEvent>,但它不是。本例中应该是HTMLAnchorElement

如何提供正确的类型?理想情况下,我会以某种方式使用C来获得正确的HTMLXyzElement

这里有一个TypeScript Playground链接,如果你想的话,你可以在这里摆弄代码

import React, { useRef } from "react";
type InferElement<T> =
T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T] extends React.DetailedHTMLProps<React.AnchorHTMLAttributes<any>, infer Elem>
? Elem
: never
: HTMLElement
type Result = InferElement<'a'> // HTMLAnchorElement
interface Props<C extends React.ElementType> {
as?: C;
children: React.ReactNode;
size?: "mini" | "small" | "medium" | "large";
onClick?: (event: React.MouseEvent<InferElement<C>, MouseEvent>) => void;
}
type ButtonProps<C extends React.ElementType> = Props<C> &
Omit<React.ComponentPropsWithRef<C>, keyof Props<C>>;
export const Button = <C extends React.ElementType = "button">({
children,
as,
size = "medium",
onClick,
...restProps
}: ButtonProps<C>) => {
const Component: React.ElementType = as || "button";

return (
<Component {...restProps} className={`button-${size}`} onClick={onClick}>
{children}
</Component>
);
};
const Link = ({ to, children }: { to: string; children: React.ReactNode }) => {
return <a href={to}>{children}</a>;
};
export const ButtonUser = () => {
return (
<div>
<Button size="mini" disabled>
Button text
</Button>
<Button as="a" size="mini" href="test" onClick={(e) => console.log(e)}>
Button text
</Button>
<Button as={Link} to="/" size="mini">
Button text
</Button>
</div>
);
};
const jsx = <Button as="a" onClick={(e) => console.log(e)}>Anchor button</Button>

由于JSX.IntrinsicElements只是html元素的大映射,您可以从JSX.IntrinsicElements推断html元素名称并将其用作回调参数。参见InferElementutil

可以用HTMLElement代替HTMLButtonElement

最新更新