我有一个组件,它根据props渲染带有文本区域或输入文本字段的JSX。我想显式地键入一个ref,以便能够附加到两种不同类型的元素(textarea或input)。我该怎么做呢?我还需要这个类型,因为当子对象接收到它时,我必须再次显式地键入props,因为我将把ref作为一个prop传递给它,以便在ref被聚焦时应用样式。
我试过了
const fieldRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
和
type FieldRefType<T extends HTMLInputElement | HTMLTextAreaElement> = {
current: T | null;
};
const fieldRef =
useRef<FieldRefType<HTMLInputElement | HTMLTextAreaElement>>(null);
但是我总是得到类似于
变化的错误Type 'RefObject<HTMLInputElement | HTMLTextAreaElement>' is not assignable to type 'LegacyRef<HTMLTextAreaElement> | undefined'.
下面是代码中相关的部分:
const FormField = ({ fieldType }: FormFieldProps): JSX.Element => {
const fieldRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);
let placeholderText: string;
switch (fieldType) {
case "Name":
placeholderText = "Enter your name";
break;
case "Email":
placeholderText = "Enter your email address";
break;
case "Subject":
placeholderText = "Enter the subject of your message";
break;
case "Message":
placeholderText = "Enter your message";
break;
default:
const _exhaustiveCheck: never = fieldType;
return _exhaustiveCheck;
}
return (
<div className={styles["label-field-container"]}>
<label className={styles["label"]} htmlFor={fieldType}>
{fieldType}
</label>
{fieldType === "Message" ? (
<textarea
className={`${styles["field"]} ${styles["field-large"]}`}
id={fieldType}
ref={fieldRef as React.RefObject<HTMLTextAreaElement>}
placeholder={placeholderText}
autoComplete="off"
required
></textarea>
) : (
<input
className={styles["field"]}
id={fieldType}
ref={fieldRef as React.RefObject<HTMLInputElement>}
type="text"
placeholder={placeholderText}
autoComplete="off"
required
></input>
)}
<FormFieldUnderline />
</div>
);
};
编辑:做ref={fieldRef as React.RefObject<HTMLTextAreaElement>}
工作,但有些东西告诉我这是一个坏主意,在这里使用类型断言。有没有更好的办法?编辑2:使用交叉点作为const fieldRef = useRef<HTMLInputElement & HTMLTextAreaElement>(null);
将工作,因为TS知道不会有任何类型错误。这将限制变量只能访问这两种类型的属性。
问题如下:typescript期望信息从您的组件流向input
或textarea
,而refs则相反:这些组件获得对对象的引用,它们将修改该对象并向您的组件提供信息。想象一下,如果fieldRef
不是一个ref,而只是一个对象,您将一些信息传递给组件<Foo info={fieldRef} />
来处理。想象一下,Foo
的info
属性类型被设置为{ current: HTMLInputElement }
,因为Foo
只知道如何与HTMLInputElement
对象一起工作,所以如果你传递{ current: HTMLInputElement | HTMLTextareaElement }
类型的对象就会出现错误:这个对象的目的是给Foo
提供信息,而Foo
不知道如何与HTMLTextareaElement
s一起工作。Refs有不同的目的,但是没有办法告诉typescript"嘿,这个对象的目的不是传递信息给input
,而是让input
修改它"。
所以要输入这个,你必须使用断言,或者传递函数ref到input
而不是ref对象。这种方式是明确的:你传递一个函数给input
或textarea
,他们将调用HTMLInputElement
或HTMLTextareaElement
,这个函数知道如何使用这两个,所以没有错误:
const fieldRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
const setRef = (e: HTMLInputElement | HTMLTextAreaElement) => fieldRef.current = e;
<input ref={setRef} />
<textarea ref={setRef} />
或者直接内联
const fieldRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
<input ref={e => fieldRef.current = e} />
<textarea ref={e => fieldRef.current = e} />