React中TypeScript定义组件props联合类型无法识别报错如何解决
问题根源
- 没有为联合类型配置可辨识属性:你参考的示例里每个联合分支都有固定字面量的
type字段,TypeScript就是靠这个字段自动收敛类型的。你当前的两个类型分支没有共同的固定值标识,TS无法自动判断当前props属于哪个分支,直接访问仅单个分支存在的属性就会报错。 - 类型定义冗余:
LayersItemStatic和LayersItemDynamic已经继承了sharedTypes,后续LayersItemProps定义时再次& sharedTypes没有实际作用。 - React.FC的默认注入干扰:
FC类型会自动给props包裹一层PropsWithChildren,注入可选的children属性,和你自定义的必填children类型冲突,也是报错里出现PropsWithChildren的原因。
修改方案
方案1:用in操作符做类型守卫(无需修改现有类型结构)
第一步:简化类型定义
export enum LayersItemOptionsEnum { OPERATOR, HEADER, } type sharedTypes = { children: string | ReactElement; }; type LayersItemStatic = sharedTypes & { label: string; option: LayersItemOptionsEnum; }; type LayersItemDynamic = sharedTypes & { currentLayer: LayersElement; }; // 去掉冗余的 & sharedTypes export type LayersItemProps = LayersItemDynamic | LayersItemStatic;
第二步:组件内增加类型判断
在访问分支独有属性前,先通过in操作符判断属性是否存在,触发TS类型收敛:
// 去掉FC,避免自动注入的children类型干扰 export const LayersItem = (props: LayersItemProps): ReactElement => { // 先判断当前props属于哪个分支 const isStatic = 'option' in props; const isHeader = isStatic ? props.option === LayersItemOptionsEnum.HEADER : false; const { isInEditMode } = useAppSelector((state) => state.editMode); const shouldRenderOptions = isInEditMode && !isHeader; const { selectedState } = useAppSelector((state) => state); const states = useAppSelector((state) => state.statesData.elements); return ( <StyledLayersItem header={isHeader}> <Row> <Col span={8} offset={1 /* todo: add offset dynamically */}> {/* 分支属性访问前加判断 */} <h1>{isStatic ? props.label : props.currentLayer.name}</h1> </Col> <Col span={8} offset={4}> {shouldRenderOptions ? ( <Form.Item className="form-item" initialValue={props.children}> <Select> {/* 仅非static分支有currentLayer,加判断避免报错 */} {!isStatic ? generateOptions({ selectedState, states, currentLayer: props.currentLayer }).map((value) => { return ( <Select.Option value={value.id} key={value.id}> {value.name} </Select.Option> ); }) : null} </Select> </Form.Item> ) : ( <>{props.children}</> )} </Col> </Row> </StyledLayersItem> ); };
方案2:增加可辨识属性(更符合你参考的示例逻辑,维护性更高)
给每个联合分支加固定字面量的标识字段,TS可以自动完成类型收敛,不需要每次用in判断:
export enum LayersItemOptionsEnum { OPERATOR, HEADER, } type sharedTypes = { children: string | ReactElement; }; type LayersItemStatic = sharedTypes & { // 可辨识字段,固定字面量 type: 'static'; label: string; option: LayersItemOptionsEnum; }; type LayersItemDynamic = sharedTypes & { // 可辨识字段,固定字面量 type: 'dynamic'; currentLayer: LayersElement; }; export type LayersItemProps = LayersItemDynamic | LayersItemStatic;
使用时直接判断type字段即可:
const isStatic = props.type === 'static'; // 后续逻辑和方案1一致
内容的提问来源于stack exchange,提问作者Alex Ironside
相关产品推荐
相关产品推荐

