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

如何让自定义React If组件支持TypeScript类型收窄?

如何让自定义React If组件支持TypeScript类型收窄?

你的问题核心在于:原有的If组件仅接收布尔值作为condition,且直接传入ReactNode作为子元素——TypeScript无法建立condition为true与子元素中props.alert已被收窄之间的关联,因为ReactNode是静态分析的,不管condition是否成立,TypeScript都会校验所有代码分支。

要解决这个问题,需要把If组件改成接收回调函数作为子元素,通过泛型关联条件判断结果与回调参数的类型,让TypeScript能在回调内部自动完成类型收窄。

方案1:重构If组件为回调式(通用版)

修改If组件的定义,让它接收一个可能为null/undefined的值作为判断条件,子元素是仅当该值存在时才执行的回调:

import React from 'react';

interface IfProps<T> {
  // 要判断的目标值,允许为null/undefined
  when: T | null | undefined;
  // 回调函数,参数为非空的目标值
  children: (value: NonNullable<T>) => React.ReactNode;
}

export default function If<T>({ when, children }: IfProps<T>) {
  // 仅当when存在时,执行回调并传入非空值
  return when != null ? children(when) : null;
}

使用方式

在Test组件中,直接传入props.alert作为when参数,回调函数的参数会自动被收窄为Alert类型:

interface Alert {
  message: {
    title: string;
    content: string;
  }
}

interface P {
  alert?: Alert;
}

function Test(props: P) {
  return (
    <div>
      <span>Lorem Ipsum</span>
      <If when={props.alert}>
        {/* alert自动被收窄为Alert类型,无类型错误 */}
        {(alert) => (
          <div>
            <span>{alert.message.title}</span>
          </div>
        )}
      </If>
    </div>
  );
}

方案2:结合自定义类型谓词函数

如果你一定要保留hasAlert这样的类型谓词函数,可以调整If组件的泛型定义,让它关联布尔条件与收窄后的类型:

import React from 'react';

interface IfProps<C extends boolean, T> {
  condition: C;
  // 仅当condition为true时,要求传入接收T类型参数的回调
  children: C extends true ? (value: T) => React.ReactNode : never;
  // 传入需要被收窄的目标值
  value: T | null | undefined;
}

export default function If<C extends boolean, T>(props: IfProps<C, T>) {
  if (props.condition) {
    return props.children(props.value as T);
  }
  return null;
}

使用方式

传入hasAlert的结果作为condition,同时传入props.alert作为value,回调中自动拿到收窄后的Alert类型:

function hasAlert(alert?: Alert): alert is Alert {
  return !!alert; // 替换为你的isEmpty逻辑
}

function Test(props: P) {
  return (
    <div>
      <span>Lorem Ipsum</span>
      <If 
        condition={hasAlert(props.alert)} 
        value={props.alert}
      >
        {(alert) => (
          <div>
            <span>{alert.message.title}</span>
          </div>
        )}
      </If>
    </div>
  );
}

为什么原方案不生效?

原If组件的children是ReactNode,TypeScript会提前解析所有子元素的代码,即使condition为false,它也会检查props.alert.message.title的合法性——而此时props.alert可能是undefined,因此报错。改用回调后,只有当条件成立时才会执行回调,TypeScript能识别到这一点,从而在回调内部安全地进行类型收窄。

内容的提问来源于stack exchange,提问作者RexiaN

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:27:46