TypeScript中react-bootstrap Button配合react-router Link的类型报错问题
问题场景
现有基于react-bootstrap编写的卡片组件代码如下:
<Card className={` ${styles.stretchCard}`}> <Card.Body className='d-flex flex-column'> <Card.Title>{post.title}</Card.Title> <Card.Text className='m-0'><span className={styles.dataTitle}>Author: </span>{post.author}</Card.Text> <Card.Text ><span className={styles.dataTitle}>Published: </span>{post.publishedDate}</Card.Text> <Card.Text>{post.shortDescription}</Card.Text> <Button as={Link} to={`post/${post.id}`} className='align-self-start mt-auto' > Read more </Button> </Card.Body> </Card>
代码功能运行完全正常,但TypeScript编译时抛出如下类型错误,报错位置指向Button的as属性:
Type 'ForwardRefExoticComponent<LinkProps & RefAttributes>' is not assignable to type '(ForwardRefExoticComponent<LinkProps & RefAttributes> & keyof IntrinsicElements) | undefined'.
Type 'ForwardRefExoticComponent<LinkProps & RefAttributes>' is not assignable to type 'ForwardRefExoticComponent<LinkProps & RefAttributes> & "symbol"'.
Type 'ForwardRefExoticComponent<LinkProps & RefAttributes>' is not assignable to type '"symbol"'.
类型安全解决方案
不需要使用any类型断言丢失全局类型检查能力,以下两种方案都可以保留完整的类型校验:
方案1:最小改动精确断言
直接对传入as的Link做两层精确类型断言,匹配React组件类型约束,不会丢失to等Link专属属性、Button通用属性的校验能力:
import type { LinkProps } from 'react-router-dom'; import { Link } from 'react-router-dom'; import { Button } from 'react-bootstrap'; // 组件内使用 <Button as={Link as unknown as React.ElementType<LinkProps>} to={`post/${post.id}`} className='align-self-start mt-auto' > Read more </Button>
该断言是安全的:运行时Link本身就是合法的React元素类型,报错本质是两个依赖库的类型泛型约束没有对齐,不存在逻辑层面的类型不兼容。断言后TS依然会对传入的属性做完整校验,比如to传空值、传非路由路径都会正常抛错。
方案2:封装通用复用组件
如果项目中多处需要使用Button渲染路由链接,可以提前封装通用组件,避免重复写类型断言:
import { Button, ButtonProps } from 'react-bootstrap'; import type { LinkProps } from 'react-router-dom'; // 全局定义一次即可 const LinkButton = Button as React.ComponentType<ButtonProps & Partial<LinkProps>>; // 业务中直接使用,自动支持Button所有属性 + Link的to等路由属性 <LinkButton to={`post/${post.id}`} className='align-self-start mt-auto' > Read more </LinkButton>
报错原因
该错误不属于代码逻辑错误,是第三方库类型定义兼容问题:react-bootstrap的as属性类型默认会交叉校验原生HTML标签的类型约束,而react-router v6的Link是forwardRef包装的组件,类型定义没有完全对齐react-bootstrap的泛型校验规则,因此抛出类型不匹配错误,实际运行时调用逻辑完全合法。
内容的提问来源于stack exchange,提问作者milosz7

