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

React组件用forwardRef遇TypeScript 2590复杂联合类型错误求助

解决React动态标签组件的TypeScript ts(2590)错误

问题根源

你的组件中DangerousHTMLProps的tag属性定义为T | ValidTags,加上泛型T本身是ValidTags的子类型,导致TypeScript需要处理一个包含所有HTML元素ref类型的超大联合类型,超出了编译器的处理上限,触发Expression produces a union type that is too complex to represent.ts(2590)错误。另外,ref的类型被简单定义为object,没有和动态标签对应的元素类型关联,也是类型推导混乱的原因。

修复方案

以下是修改后的代码,通过精准约束泛型和ref类型解决问题:

/**
 * This component is responsible for adding the innerHtml in the html element using dangerouslySetInnerHTML with html sanitization.
 */
import React, { HTMLAttributes, forwardRef, memo } from 'react';
import { isObjectEmpty, isStrEmpty, sanitizeHtml } from './helpers';
import withErrorBoundary from './withErrorBoundary';

// All valid HTML tags like 'div' | 'form' | 'a' | ...
type ValidTags = keyof React.JSX.IntrinsicElements;

// 获取标签对应的元素类型
type ElementForTag<T extends ValidTags> = React.JSX.IntrinsicElements[T]['ref'] extends React.Ref<infer E> ? E : never;

// Generic type to generate HTML props based on its tag
export type DangerousHTMLProps<T extends ValidTags> = {
    tag?: T; // 移除多余的ValidTags联合,泛型已约束T属于ValidTags
    htmlString: string;
    sanitizeProps?: object;
} & HTMLAttributes<ElementForTag<T>>; // 关联标签对应的元素属性类型

const defaultTag = 'div' as const;
function DangerousHTMLComponent<T extends ValidTags = typeof defaultTag>(
    {
        tag = defaultTag,
        htmlString,
        sanitizeProps = {},
        ...rest
    }: DangerousHTMLProps<T>,
    ref: React.Ref<ElementForTag<T>> // 让ref类型和标签元素匹配
): React.ReactElement {
    const ParentElem = tag as keyof React.JSX.IntrinsicElements;
    const sanitizePropsObj = isObjectEmpty(sanitizeProps) ? {} : sanitizeProps;

    if (isStrEmpty(htmlString)) {
        return <ParentElem {...rest} ref={ref} />;
    }

    return (
        <ParentElem
            {...rest}
            ref={ref}
            dangerouslySetInnerHTML={{ __html: sanitizeHtml(htmlString, sanitizePropsObj) }}
        />
    );
}

const DangerousHTML = forwardRef(DangerousHTMLComponent);
export default memo(DangerousHTML);

关键修改点

  • 修正tag的类型定义:把tag?: T | ValidTags改为tag?: T,因为泛型T已经被约束为ValidTags的子类型,不需要额外的联合类型,减少类型复杂度。
  • 新增ElementForTag类型:通过条件类型从标签的固有元素类型中提取对应的DOM元素类型,让ref和HTMLAttributes都能精准匹配当前标签的元素类型。
  • 关联ref的类型:将组件的第二个参数ref的类型改为React.Ref<ElementForTag<T>>,确保ref类型和动态标签的元素类型一致,避免模糊的object类型导致的推导混乱。
  • 简化默认值处理:把defaultTag标记为const,让TypeScript推导更精确;同时简化sanitizePropsObj的逻辑,避免不必要的类型断言。

这样修改后,TypeScript可以精准推导每个标签对应的元素和ref类型,不会再生成过于复杂的联合类型,解决ts(2590)错误的同时,也让组件的类型安全性更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:06:14