打字稿:React.forwardRef 带有来自@material-ui 的通用道具
Posted
技术标签:
【中文标题】打字稿:React.forwardRef 带有来自@material-ui 的通用道具【英文标题】:Typescript: React.forwardRef with generic props from @material-ui 【发布时间】:2020-10-19 21:28:27 【问题描述】:我正在尝试在@material-ui Button 组件之上添加自定义逻辑。为此,我正在创建一个包装原始 Button 并正常运行的组件。
我的问题是正确输入,尤其是从原始组件中获取道具。我一直关注https://material-ui.com/guides/typescript/#usage-of-component-prop,直到我添加了React.forwardRef()
。
没有forwardRef
的工作版:
import Button, ButtonProps from '@material-ui/core'
import React from 'react'
type ExtraProps =
target?: string
rel?: string
href?: string
color?: 'default' | 'primary' | 'secondary'
// see https://material-ui.com/guides/typescript/#usage-of-component-prop
type Props<C extends React.ElementType> = ButtonProps<C, component?: C > & ExtraProps
const CustomButton = <C extends React.ElementType>(
children,
target,
rel,
href,
...rest
: Props<C>) =>
const relValue = target === '_blank' ? 'noopener noreferrer' : rel
const linkValues = href ? href, target, rel: relValue : undefined
return (
<Button ...linkValues ...rest>
children
</Button>
)
带有React.forwardRef
的非工作版本:
import Button, ButtonProps from '@material-ui/core'
import React from 'react'
type ExtraProps =
target?: string
rel?: string
href?: string
color?: 'default' | 'primary' | 'secondary'
// see https://material-ui.com/guides/typescript/#usage-of-component-prop
export type Props<C extends React.ElementType> = ButtonProps<C, component?: C > & ExtraProps
const CustomButton = React.forwardRef<htmlButtonElement, Props<React.ElementType>>(
( children, target, rel, href, ...rest , ref) =>
const relValue = target === '_blank' ? 'noopener noreferrer' : rel
const linkValues = href ? href, target, rel: relValue : undefined
return (
<Button ...linkValues ...rest ref=ref>
children
</Button>
)
)
当我说“不工作”时,是因为CustomButton
的类型是:
React.ForwardRefExoticComponent<Pick<Props<React.ElementType<any>>, string | number | symbol> & React.RefAttributes<HTMLButtonElement>>
而不是
<C extends React.ElementType<any>>( children, target, rel, href, ...rest : Props<C>) => JSX.Element
这意味着我可以将任何道具传递给我的CustomButton
,而 TS 根本不会执行合同。
我应该如何修复带有React.forwardRef()
的版本才能正确输入?
Codesandbox 演示:https://codesandbox.io/s/boring-hertz-0hutt?file=/src/App.tsx
【问题讨论】:
【参考方案1】:forwardRef 不是通用的。这是万恶之源。我也在努力实现类型安全。现在我有这个解决方案:
type As = 'button' | 'a' | 'span';
type Element<T extends As = 'button'> = T extends 'span'
? HTMLSpanElement
: T extends 'a'
? HTMLAnchorElement
: HTMLButtonElement;
interface CommonButtonProps<T extends As>
view?: View;
size?: Size;
disabled?: boolean;
iconLeft?: ReactElement;
iconRight?: ReactElement;
className?: string;
progress?: boolean;
baseElement?: T;
export type ButtonProps<T extends As = 'button'> = CommonButtonProps<T> &
(T extends 'a'
? JSX.IntrinsicElements['a']
: T extends 'span'
? JSX.IntrinsicElements['span']
: T extends undefined
? JSX.IntrinsicElements['button']
: JSX.IntrinsicElements['button']);
const Button = (
children, baseElement, ...props : PropsWithChildren<ButtonProps<As>>,
ref: React.Ref<Element<As>>,
) =>
return (
<StyledButton ...props as=baseElement ref=ref>
children
</StyledButton>
);
;
function genericForwardRef<T extends ForwardRefRenderFunction<any, any>>(Component: T)
const ForwardedComponent = forwardRef(Component);
return <U extends As = 'button'>(
props: PropsWithChildren<PropsWithoutRef<ButtonProps<U>> & RefAttributes<Element<U>>>,
) => <ForwardedComponent ...props />;
export default genericForwardRef(Button);
(注:我正在使用样式组件)
【讨论】:
以上是关于打字稿:React.forwardRef 带有来自@material-ui 的通用道具的主要内容,如果未能解决你的问题,请参考以下文章
如何防止 VS 2022 在 esproj 中显示带有 d.ts 文件的打字稿错误