如何修复自定义React组件的‘Property htmlFor does not exist’错误?
问题描述
我实现了一个React自定义组件IconButton,代码如下:
import React from "react"; import classnames from 'classnames'; import { ButtonVariantColor } from '../types/button'; export type IconButtonProps = { element?: React.ElementType icon: React.ElementType variantColor?: ButtonVariantColor | string | undefined } & React.HTMLAttributes<HTMLButtonElement & HTMLLabelElement> export default function IconButton({ className, element = 'button', icon, variantColor = ButtonVariantColor.default, ...props }:IconButtonProps) { const Icon = icon; const ButtonElement = element; if (!(variantColor in ButtonVariantColor)) { variantColor = ButtonVariantColor.default; } return ( <ButtonElement className={ classnames('btn btn-circle border-none text-base-content', { 'bg-inherit hover:bg-gray-300 dark:bg-gray-800 dark:hover:bg-gray-700': variantColor === ButtonVariantColor.default, 'bg-blue-200 hover:bg-blue-400 dark:bg-blue-900 dark:hover:bg-blue-700': variantColor === ButtonVariantColor.info, 'bg-green-300 hover:bg-green-500 dark:bg-green-900 dark:hover:bg-green-700': variantColor === ButtonVariantColor.success, 'bg-orange-200 hover:bg-orange-400 dark:bg-orange-700 dark:hover:bg-orange-500': variantColor === ButtonVariantColor.warning, 'bg-red-200 hover:bg-red-400 dark:bg-red-900 dark:hover:bg-red-600': variantColor === ButtonVariantColor.error, }, className) } { ...props }> <Icon /> </ButtonElement> ); };
当我以如下方式使用组件时:
<IconButton element="label" icon={ ChevronLeftIcon } htmlFor={ menuId } />
TypeScript提示Property htmlFor does not exist错误。明明Props已经继承了HTMLButtonElement和HTMLLabelElement的属性,为什么还会出现这个问题?
问题原因与修复方案
原因分析
你当前用React.HTMLAttributes<HTMLButtonElement & HTMLLabelElement>的写法,实际是取两个元素属性的交集,而非并集。因为HTMLButtonElement本身没有htmlFor属性,所以交集里自然不包含该属性,TypeScript因此报错。
修复方案
这里提供两种实用的修复方式,按需选择:
方案1:联合类型区分元素属性
重新定义Props为联合类型,明确不同element对应的合法属性:
import React from "react"; import classnames from 'classnames'; import { ButtonVariantColor } from '../types/button'; // 基础公共属性 type BaseIconButtonProps = { icon: React.ElementType variantColor?: ButtonVariantColor | string | undefined }; // button元素专属Props type ButtonIconButtonProps = BaseIconButtonProps & { element?: 'button' } & React.ButtonHTMLAttributes<HTMLButtonElement>; // label元素专属Props type LabelIconButtonProps = BaseIconButtonProps & { element: 'label' } & React.LabelHTMLAttributes<HTMLLabelElement>; // 合并为联合类型,自动匹配元素属性 export type IconButtonProps = ButtonIconButtonProps | LabelIconButtonProps; export default function IconButton({ className, element = 'button', icon, variantColor = ButtonVariantColor.default, ...props }: IconButtonProps) { const Icon = icon; const ButtonElement = element; if (!(variantColor in ButtonVariantColor)) { variantColor = ButtonVariantColor.default; } return ( <ButtonElement className={ classnames('btn btn-circle border-none text-base-content', { 'bg-inherit hover:bg-gray-300 dark:bg-gray-800 dark:hover:bg-gray-700': variantColor === ButtonVariantColor.default, 'bg-blue-200 hover:bg-blue-400 dark:bg-blue-900 dark:hover:bg-blue-700': variantColor === ButtonVariantColor.info, 'bg-green-300 hover:bg-green-500 dark:bg-green-900 dark:hover:bg-green-700': variantColor === ButtonVariantColor.success, 'bg-orange-200 hover:bg-orange-400 dark:bg-orange-700 dark:hover:bg-orange-500': variantColor === ButtonVariantColor.warning, 'bg-red-200 hover:bg-red-400 dark:bg-red-900 dark:hover:bg-red-600': variantColor === ButtonVariantColor.error, }, className) } { ...props as any }> <Icon /> </ButtonElement> ); };
当传入element="label"时,TypeScript会自动匹配LabelIconButtonProps,htmlFor属性将被识别为合法。
方案2:泛型动态推导属性
如果需要支持更多自定义元素类型,泛型方案更灵活,能根据element自动推导对应属性:
import React from "react"; import classnames from 'classnames'; import { ButtonVariantColor } from '../types/button'; export type IconButtonProps<T extends React.ElementType = 'button'> = { element?: T icon: React.ElementType variantColor?: ButtonVariantColor | string | undefined } & React.ComponentPropsWithoutRef<T>; export default function IconButton<T extends React.ElementType = 'button'>({ className, element = 'button' as T, icon, variantColor = ButtonVariantColor.default, ...props }: IconButtonProps<T>) { const Icon = icon; const ButtonElement = element; if (!(variantColor in ButtonVariantColor)) { variantColor = ButtonVariantColor.default; } return ( <ButtonElement className={ classnames('btn btn-circle border-none text-base-content', { 'bg-inherit hover:bg-gray-300 dark:bg-gray-800 dark:hover:bg-gray-700': variantColor === ButtonVariantColor.default, 'bg-blue-200 hover:bg-blue-400 dark:bg-blue-900 dark:hover:bg-blue-700': variantColor === ButtonVariantColor.info, 'bg-green-300 hover:bg-green-500 dark:bg-green-900 dark:hover:bg-green-700': variantColor === ButtonVariantColor.success, 'bg-orange-200 hover:bg-orange-400 dark:bg-orange-700 dark:hover:bg-orange-500': variantColor === ButtonVariantColor.warning, 'bg-red-200 hover:bg-red-400 dark:bg-red-900 dark:hover:bg-red-600': variantColor === ButtonVariantColor.error, }, className) } { ...props }> <Icon /> </ButtonElement> ); };
这个方案支持任意React元素类型,传入element="label"时,htmlFor会被自动识别为合法属性。
内容的提问来源于stack exchange,提问作者Yanick Rochon
相关产品推荐
相关产品推荐

