You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何改写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 07:02:34