如何改写React条件包装组件以实现TypeScript类型收窄?
React条件包装组件的TypeScript类型自动收窄实现
问题场景
现有如下React条件包装组件:
import { ReactNode, ReactElement, FunctionComponent, PropsWithChildren } from 'react'; interface Props { condition: boolean; wrapper: (children: ReactNode) => ReactElement; } const ConditionalWrapper: FunctionComponent<PropsWithChildren<Props>> = ({ condition, wrapper, children }) => condition ? wrapper(children) : <>{children}</>;
当传入tooltip !== undefined作为condition时,wrapper回调中tooltip的类型仍为string | undefined,但此时条件已确保它是string类型。由于ESLint禁用非空断言,需要修改组件实现自动类型收窄。
可行方案
方案一:泛型关联条件与回调类型
通过泛型将condition的布尔值与wrapper的类型上下文关联,让TypeScript自动推断类型:
import { ReactNode, ReactElement, FunctionComponent, PropsWithChildren } from 'react'; interface Props<T extends boolean> { condition: T; wrapper: T extends true ? (children: ReactNode) => ReactElement : never; } const ConditionalWrapper: FunctionComponent<PropsWithChildren<Props<boolean>>> = ({ condition, wrapper, children }) => condition ? wrapper(children) : <>{children}</>;
使用时,TypeScript会根据condition的布尔字面量类型自动收窄wrapper的上下文:
const tooltip: string | undefined = "提示文本"; <ConditionalWrapper condition={tooltip !== undefined} wrapper={() => ( // 此处tooltip已被自动收窄为string类型 <Tooltip title={tooltip}>按钮</Tooltip> )} > <Button>悬停查看</Button> </ConditionalWrapper>
方案二:基于类型守卫的组件
如果需要直接在wrapper中接收已收窄的值,可以通过类型守卫函数实现:
import { ReactNode, ReactElement, FunctionComponent, PropsWithChildren } from 'react'; interface Props<T> { value: T; // 类型守卫函数,断言value为非空类型 isSafe: (val: T) => val is Exclude<T, undefined | null>; wrapper: (safeValue: Exclude<T, undefined | null>, children: ReactNode) => ReactElement; } const SafeConditionalWrapper: FunctionComponent<PropsWithChildren<Props<any>>> = ({ value, isSafe, wrapper, children }) => isSafe(value) ? wrapper(value, children) : <>{children}</>;
使用示例:
const tooltip: string | undefined = "提示文本"; <SafeConditionalWrapper value={tooltip} isSafe={(val): val is string => val !== undefined} wrapper={(safeTooltip) => ( <Tooltip title={safeTooltip}>按钮</Tooltip> )} > <Button>悬停查看</Button> </SafeConditionalWrapper>
原理说明
- 方案一利用泛型条件类型,当
condition为true时,TypeScript会将wrapper的上下文标记为安全,从而自动收窄外部变量的类型。 - 方案二通过用户定义的类型守卫,直接在条件判断中完成类型断言,确保
wrapper接收的参数是已收窄的安全类型,完全符合TypeScript的类型推导规则,无需非空断言。
内容的提问来源于stack exchange,提问作者Robert Koritnik
相关产品推荐
相关产品推荐

