React PropTypes结合defaultProps与InferProps的类型兼容问题
解决React 17.0.2中propTypes、defaultProps与InferProps的类型冲突问题
问题场景
以下是出现类型错误的代码示例:
Container.tsx
import PropTypes from 'prop-types'; import { InferProps } from 'prop-types'; const InfoPropType = { legend: PropTypes.string.isRequired, value: PropTypes.number, }; const ContainerPropTypes = { config: PropTypes.shape({ c1: PropTypes.shape(InfoPropType).isRequired, }), }; type ContainerType = InferProps<typeof ContainerPropTypes>; const ContainerDefaultProps = { config: { c1: { value: 1 } } }; const Container = (props: ContainerType & typeof ContainerDefaultProps): JSX.Element => { const { config: cConfig } = props; return ( <div> {cConfig.c1.legend} has {cConfig.c1.value} </div> ); }; Container.propTypes = ContainerPropTypes; Container.defaultProps = ContainerDefaultProps;
App.tsx
import Container from './Container'; type ConfigType = { c1: { legend: string; value?: number; } }; const App = (): JSX.Element => { const aConfig: ConfigType = { c1: { legend: 'l1' } }; return (<Container config={aConfig} />); // 此处出现类型错误 }; export default App;
错误信息
类型“ConfigType”无法赋值给类型“InferPropsInner<Pick<{ c1: Validator<NonNullable<InferProps<{ legend: Validator<string>; value: Requireable<number>; }>>>; }, "c1">> & Partial<...> & { ...; }”。 类型“ConfigType”无法赋值给类型“{ c1: { value: number; }; }”。 两者的“c1.value”类型不兼容。 类型“number | undefined”无法赋值给类型“number”。 类型“undefined”无法赋值给类型“number”。 预期类型来自属性“config”,该属性在类型“IntrinsicAttributes & Pick<Pick<InferPropsInner<Pick<{ config: Requireable<InferProps<{ c1: Validator<NonNullable<InferProps<{ legend: Validator<string>; value: Requireable<number>; }>>>; }>>>; }, never>> & Partial<...> & { ...; }, "config"> & Pick<...> & Pick<...>, never> & Partial<...> & Partial<...>'上声明
错误核心是:传入的aConfig中value为可选字段,但组件props类型却要求value为必填,导致类型不匹配。
解决方案
1. 修正组件Props类型定义
错误根源是将ContainerType与typeof ContainerDefaultProps交叉作为props类型,强制要求外部传入value字段。只需去掉这个交叉,仅使用InferProps推断的类型即可:
// Container.tsx 修改后 const Container = (props: ContainerType): JSX.Element => { const { config: cConfig } = props; // 利用Nullish合并运算符处理可能的undefined,同时依赖defaultProps兜底 return ( <div> {cConfig.c1.legend} has {cConfig.c1.value ?? ContainerDefaultProps.config.c1.value} </div> ); };
2. 优化组件内部类型安全(可选)
如果希望TypeScript直接识别到cConfig.c1.value不会是undefined,可以手动合并类型,同时在组件内部合并props与defaultProps:
// Container.tsx 优化版 type ContainerProps = Partial<typeof ContainerDefaultProps> & ContainerType; const Container = (props: ContainerProps): JSX.Element => { // 合并传入的props与默认值,确保内部使用的是完整配置 const mergedConfig = { ...ContainerDefaultProps.config, ...props.config }; return ( <div> {mergedConfig.c1.legend} has {mergedConfig.c1.value} </div> ); };
3. 验证结果
修改后,App.tsx中的ConfigType与组件config属性的类型完全匹配,类型错误会自动消失。
原理说明
InferProps会根据propTypes正确推断出value为可选类型(number | undefined)。defaultProps的作用是在组件内部为可选属性提供默认值,不需要通过类型交叉强制外部传入该属性。- 错误的类型交叉会覆盖
InferProps的正确推断,导致可选属性被标记为必填。
内容的提问来源于stack exchange,提问作者gaitat
相关产品推荐
相关产品推荐

