React TypeScript条件Props传参时类型不匹配报错如何解决
我正在开发一个接收inside和position两个props的组件,规则如下:
- 当
inside值为true时,position仅支持left、bottom两个取值 - 当
inside值为false时,position支持left、bottom、right、top四个取值
初始实现代码如下:
type InsidePosition = "bottom" | "left" type OutsidePosition = "top" | "right" | InsidePosition type CoordinatesProps = | { inside: true, position: InsidePosition} | { inside: false, position: OutsidePosition} function Coordinates({ position, inside } : CoordinatesProps) { const CoordinateComponent = inside ? InsideCoordinate : OutsideCoordinate; return _.times(8, (index: number) => { return <CoordinateComponent key={ `${ position }-${ index }` } index={ index } position={ position } />; }); }
子组件InsideCoordinate和OutsideCoordinate的定义如下:
type InsideCoordinateProps = { position: InsidePosition, index: number, }; function InsideCoordinate({ position, index }: InsideCoordinateProps) { // 组件业务逻辑 } type OutsideCoordinateProps = { position: OutsidePosition, index: number, }; function OutsideCoordinate({ position, index }: OutsideCoordinateProps) { // 组件业务逻辑 }
报错信息
给CoordinateComponent传递position={ position }属性时,触发TypeScript类型报错:
typescript: Type '"bottom" | "left" | "top" | "right"' is not assignable to type '"bottom" | "left"'. Type '"top"' is not assignable to type '"bottom" | "left"'. [2322]
你的联合类型定义本身没有错误,报错核心是TypeScript不会跟踪三元表达式赋值的变量和原解构变量之间的类型关联。
当执行const CoordinateComponent = inside ? InsideCoordinate : OutsideCoordinate时,TS只会将该变量推导为两个组件类型的联合:typeof InsideCoordinate | typeof OutsideCoordinate,同时该作用域下position的类型是四个取值的全量联合。TS不支持跨变量的类型关联收窄,不会自动识别「选中InsideCoordinate时position必然是InsidePosition、选中OutsideCoordinate时position必然是OutsidePosition」的对应关系,校验props时会检查所有可能的组件是否能接收所有可能的position值,自然会抛出top/right无法传给InsideCoordinate的错误。
不需要使用类型断言,有两种类型安全的实现方案:
方案1:在分支内直接渲染对应组件(推荐)
放弃中间组件变量,把判断逻辑放到JSX分支中,TS可以在同一个代码块内自动完成类型收窄:
function Coordinates({ position, inside } : CoordinatesProps) { return _.times(8, (index: number) => { return inside ? ( <InsideCoordinate key={`${position}-${index}`} index={index} position={position} /> ) : ( <OutsideCoordinate key={`${position}-${index}`} index={index} position={position} /> ) }); }
这种写法下,inside为true的分支内TS会自动收窄position类型为InsidePosition,完全匹配子组件的props要求,不会触发类型报错。
方案2:提前收窄类型再赋值组件变量
如果确实需要抽离组件变量,可以先通过判别式收窄props类型,再给变量赋值:
function Coordinates(props : CoordinatesProps) { let CoordinateComponent: typeof InsideCoordinate | typeof OutsideCoordinate; let position: InsidePosition | OutsidePosition; if (props.inside) { CoordinateComponent = InsideCoordinate; position = props.position; // 此处TS自动识别position为InsidePosition类型 } else { CoordinateComponent = OutsideCoordinate; position = props.position; // 此处TS自动识别position为OutsidePosition类型 } return _.times(8, (index) => { return <CoordinateComponent key={ `${ position }-${ index }` } index={ index } position={ position } />; }); }
该写法冗余度更高,日常开发优先选择第一种方案即可。
内容的提问来源于stack exchange,提问作者LandonSchropp

