TypeScript报错:类型缺少'to'属性,如何无需条件渲染解决?
解决TypeScript无法识别条件props的问题
问题原因
TypeScript无法将typeof url == 'string'这个运行时条件,同时关联到动态选择的Tag类型(Link或'a')和展开的props对象。TS会把Tag的类型推断为typeof Link | 'a',要求props必须满足两者的属性交集,但<a>标签不存在to属性,因此抛出Property 'to' is missing in type错误。
无需条件渲染的解决方案
方案1:用as const收窄Tag类型+类型断言
通过as const让TS明确Tag的具体类型,再用satisfies约束props的类型,最后断言props匹配当前Tag的要求:
import { Link, LinkProps } from 'react-router-dom' // 用as const收窄Tag的类型,让TS知道它是Link或'a'的具体类型 const Tag = (typeof url === 'string' ? Link : 'a') as const; // 用satisfies约束props符合对应元素的属性规范 const elementProps = typeof url === 'string' ? { to: url, onClick(event: React.MouseEvent<HTMLAnchorElement>) { setActive(true); close(url); event.preventDefault(); } } satisfies LinkProps : { onClick() { setActive(true); onClick(); } } satisfies React.AnchorHTMLAttributes<HTMLAnchorElement>; return ( <Tag className={u.joinLaden([styles.pill, active && styles.active], ' ')} {...elementProps as typeof elementProps} > {children} </Tag> );
方案2:使用React.createElement创建元素
React.createElement能根据传入的组件/标签类型自动推断props的类型,完美匹配动态Tag的要求:
import { Link } from 'react-router-dom' import React from 'react'; const Tag = typeof url === 'string' ? Link : 'a'; // 抽离公共props,避免重复 const commonProps = { className: u.joinLaden([styles.pill, active && styles.active], ' ') }; const elementProps = typeof url === 'string' ? { ...commonProps, to: url, onClick(event: React.MouseEvent<HTMLAnchorElement>) { setActive(true); close(url); event.preventDefault(); } } : { ...commonProps, onClick() { setActive(true); onClick(); } }; // React.createElement会自动根据Tag类型校验props return React.createElement(Tag, elementProps, children);
补充:优雅的条件渲染方案
如果接受轻量条件渲染,可将公共内容抽成子组件,减少重复代码,同时让TS类型推断更清晰:
import { Link } from 'react-router-dom' // 抽离公共内容组件 const PillContent = ({ children }: { children: React.ReactNode }) => ( <> {children} </> ); return typeof url === 'string' ? ( <Link className={u.joinLaden([styles.pill, active && styles.active], ' ')} to={url} onClick={(event) => { setActive(true); close(url); event.preventDefault(); }} > <PillContent>{children}</PillContent> </Link> ) : ( <a className={u.joinLaden([styles.pill, active && styles.active], ' ')} onClick={() => { setActive(true); onClick(); }} > <PillContent>{children}</PillContent> </a> );
内容的提问来源于stack exchange,提问作者Ricardo
相关产品推荐
相关产品推荐

