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

如何用TypeScript泛型解决React不同元素的Ref类型兼容问题?

React + TypeScript 泛型组件Ref类型不匹配问题解决

问题场景

我尝试实现以下简化版组件,根据multiline属性值渲染<input>或<textarea>,计划通过TypeScript泛型传入HTMLInputElement或HTMLTextAreaElement来指定inputRef的类型:

import React, { ReactElement } from 'react';

import { LegacyRef } from 'react';

export interface TestProps<T> {
  inputRef?: LegacyRef<T>;
  multiline?: boolean;
}

export const Test = <T,>({ inputRef, multiline }: TestProps<T>): ReactElement | null => (
  <>
    {multiline ? <textarea ref={inputRef} /> : <input ref={inputRef} type="text" />}
  </>
);

但TypeScript编译器无法识别inputRef会随multiline值对应不同类型,在ref行出现如下报错:

Type 'LegacyRef<T>' is not assignable to type 'LegacyRef<HTMLTextAreaElement>'
Type 'LegacyRef<T>' is not assignable to type 'LegacyRef<HTMLInputElement>'

解决方案

方案1:用条件类型关联泛型与multiline属性

通过条件类型让泛型T的类型与multiline的布尔值强关联,让编译器能准确推断对应Ref类型:

import React, { ReactElement, LegacyRef } from 'react';

// 条件类型:multiline为true时T必须是HTMLTextAreaElement,否则为HTMLInputElement
type TestProps<T extends HTMLInputElement | HTMLTextAreaElement> = 
  T extends HTMLTextAreaElement 
  ? { inputRef?: LegacyRef<T>; multiline: true } 
  : { inputRef?: LegacyRef<T>; multiline?: false };

export const Test = <T extends HTMLInputElement | HTMLTextAreaElement>(
  { inputRef, multiline }: TestProps<T>
): ReactElement | null => (
  <>
    {multiline ? (
      <textarea ref={inputRef as LegacyRef<HTMLTextAreaElement>} />
    ) : (
      <input ref={inputRef as LegacyRef<HTMLInputElement>} type="text" />
    )}
  </>
);

方案2:使用函数重载明确参数类型

通过函数重载定义不同multiline值对应的参数结构,让类型检查更精准:

import React, { ReactElement, LegacyRef } from 'react';

// 重载1:multiline为true时,Ref对应HTMLTextAreaElement
function Test(props: { inputRef?: LegacyRef<HTMLTextAreaElement>; multiline: true }): ReactElement | null;
// 重载2:multiline为false或省略时,Ref对应HTMLInputElement
function Test(props: { inputRef?: LegacyRef<HTMLInputElement>; multiline?: false }): ReactElement | null;
// 组件实现
function Test({ inputRef, multiline }: { inputRef?: LegacyRef<any>; multiline?: boolean }): ReactElement | null {
  return (
    <>
      {multiline ? <textarea ref={inputRef} /> : <input ref={inputRef} type="text" />}
    </>
  );
}

export default Test;

方案3:分支内直接类型断言

如果不想修改Props结构,可在渲染分支中对inputRef做类型断言,绕过编译器的类型检查(需确保使用时multiline与泛型T匹配):

import React, { ReactElement, LegacyRef } from 'react';

export interface TestProps<T extends HTMLInputElement | HTMLTextAreaElement> {
  inputRef?: LegacyRef<T>;
  multiline?: boolean;
}

export const Test = <T extends HTMLInputElement | HTMLTextAreaElement>(
  { inputRef, multiline }: TestProps<T>
): ReactElement | null => (
  <>
    {multiline ? (
      <textarea ref={inputRef as LegacyRef<HTMLTextAreaElement>} />
    ) : (
      <input ref={inputRef as LegacyRef<HTMLInputElement>} type="text" />
    )}
  </>
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 18:25:30