React Storybook+TypeScript中提取组件Props类型显示any的问题排查
React组件Props类型提取异常问题分析与解决方案
问题背景
在Storybook中开发React Button组件时,尝试通过React.ComponentProps<typeof Button>提取组件的Props类型,结果始终得到any类型。相关代码如下:
Button组件代码
import React, { useCallback } from 'react'; import { ButtonProps } from './types'; const Button = (props: ButtonProps) => { const { children, extraClasses, onClick } = props; const memoizedOnClick = useCallback(() => onClick?.(props), []); return ( <button className={extraClasses} onClick={memoizedOnClick}> {children} </button> ); }; Button.defaultProps = { children: <b>Test Button</b> }; export default Button;
尝试提取类型的代码
type DefaultButtonProps = React.ComponentProps<typeof Button>
ButtonProps定义
export interface ButtonProps extends HTMLButtonElement { children: ReactNode, extraClasses?: string; onClick?: (props: ButtonProps) => void }
原因分析
- 错误的继承目标:
ButtonProps继承了HTMLButtonElement,这是DOM按钮元素的实例类型,而非React按钮组件接收的Props类型。React组件的Props类型应该使用官方封装的ButtonHTMLAttributes<HTMLButtonElement>,直接继承DOM实例类型会导致TypeScript无法正确推导组件Props,最终React.ComponentProps返回any。 - 循环引用干扰:
onClick的参数定义为ButtonProps本身,形成了类型循环引用,进一步破坏了TypeScript的类型解析逻辑。
修复方案
1. 修正ButtonProps的定义
将继承目标替换为React官方的按钮Props类型,并消除循环引用:
import { ReactNode, ButtonHTMLAttributes } from 'react'; export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { children: ReactNode; extraClasses?: string; // 用Omit移除onClick,避免循环引用 onClick?: (props: Omit<ButtonProps, 'onClick'>) => void; }
ButtonHTMLAttributes<HTMLButtonElement>包含了所有原生HTML按钮的Props(如disabled、type、name等),符合React组件Props的设计规范。- 使用
Omit<ButtonProps, 'onClick'>作为onClick的参数类型,避免了循环引用问题。
2. 验证类型提取
此时再执行以下代码,DefaultButtonProps会正确解析为ButtonProps的完整类型:
import Button from './Button'; type DefaultButtonProps = React.ComponentProps<typeof Button>;
额外优化:修复useCallback依赖问题
原组件中useCallback的依赖数组为空,但内部使用了props和onClick,会导致闭包捕获旧值,引发逻辑错误。修正为:
const memoizedOnClick = useCallback(() => onClick?.(props), [onClick, props]);
内容的提问来源于stack exchange,提问作者Saif
相关产品推荐
相关产品推荐

