基于道具的动态标签



我希望能够接收道具,这些道具是<button>的道具或<a>的道具的并集类型。然后,我想呈现相关组件。

我尝试了以下(TypeScript Playground(:

import React from 'react'
type MyButtonProps = (
| ({ tag: "button" } & React.ButtonHTMLAttributes<HTMLButtonElement>)
| ({ tag: "a" } & React.AnchorHTMLAttributes<HTMLAnchorElement>)
)
export const MyButton = ({ tag, ...props }: MyButtonProps) => (
tag === "a"
? <a {...props} />
: <button {...props} />
)

这会产生以下错误:

Type '{ autoFocus?: boolean | undefined; disabled?: boolean | undefined; form?: string | undefined; formAction?: string | undefined; formEncType?: string | undefined; formMethod?: string | undefined; ... 258 more ...; onTransitionEndCapture?: TransitionEventHandler<...> | undefined; } | { ...; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
Type '{ download?: any; href?: string | undefined; hrefLang?: string | undefined; media?: string | undefined; ping?: string | undefined; rel?: string | undefined; target?: HTMLAttributeAnchorTarget | undefined; ... 255 more ...; onTransitionEndCapture?: TransitionEventHandler<...> | undefined; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
Type '{ download?: any; href?: string | undefined; hrefLang?: string | undefined; media?: string | undefined; ping?: string | undefined; rel?: string | undefined; target?: HTMLAttributeAnchorTarget | undefined; ... 255 more ...; onTransitionEndCapture?: TransitionEventHandler<...> | undefined; }' is not assignable to type 'ButtonHTMLAttributes<HTMLButtonElement>'.
Types of property 'type' are incompatible.
Type 'string | undefined' is not assignable to type '"button" | "submit" | "reset" | undefined'.
Type 'string' is not assignable to type '"button" | "submit" | "reset" | undefined'.(2322)

MyButton组件的预期用途如下:

const App = () => (
<>
<MyButton tag="button" onClick={doSomething}>I'm a button</MyButton>
<MyButton tag="a" href="http://example.com" target="_blank">I'm a link</MyButton>
</>
)

由于我测试了href道具的值,以确定是否应该呈现<a>元素而不是<button>元素,因此我最终更改了href道具的类型以使其成为必需,如下所示:

import React from 'react'
type MyButtonProps = (
| React.ComponentProps<"button">
| React.ComponentProps<"a"> & { href: string }
)
export const MyButton = (props: MyButtonProps) => (
'href' in props
? <a {...props} />
: <button {...props} />
)

href道具的原始类型是string | undefined,这对我来说不起作用,因为如果我渲染<a>,那么我肯定知道我得到了href的值。

既然你知道MyButton正在接收道具,为什么不把它分成两个组件呢?

import React from 'react'
type MyButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>
type MyAnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>
export const MyButton = (props: MyButtonProps) => <button {...props} />
export const MyAnchor = (props: MyAnchorProps) => <a {...props} />

您对button元素并没有真正的类型推断,您可以强制转换它:

import React from 'react'
type MyButtonProps = (
| React.ButtonHTMLAttributes<HTMLButtonElement>
| React.AnchorHTMLAttributes<HTMLAnchorElement>
)
export const MyButton = (props: MyButtonProps) => {
if ('href' in props) {
return <a {...props} />
}
return <button {...(props as React.ButtonHTMLAttributes<HTMLButtonElement>)} />;
}

操场

编辑

此外,您有可能覆盖(或覆盖元素道具(元素本身的type键,您应该为其提供唯一的命名和排序,以使干扰更清晰:

import React from 'react'
type MyButtonProps = (
| React.ButtonHTMLAttributes<HTMLButtonElement> & { componentType: "a" }
| React.AnchorHTMLAttributes<HTMLAnchorElement> & { componentType: "b" }
)
export const MyButton = (props: MyButtonProps) => {
if (props.componentType === "b") {
const { componentType, ...rest } = props;
return <a {...rest} />
}
const { componentType, ...rest } = props;
return <button {...(rest)} />;
}

操场

最新更新