Как типизировать forwardRef?

Имеется такой компонент:

import { forwardRef, Ref } from "react";

export interface ICelebrationScreenProps
{
    title: string;
    [ prop: string ]: any;
}

function CelebrationScreen({ title, ...props }: ICelebrationScreenProps, ref: Ref<HTMLDivElement>)
{
    return (<>{title}</>);
};

export default forwardRef(CelebrationScreen);

Когда я пытаюсь использовать данный компонент в другом файле, VSCode мне не дает подсказок и ошибок о пропсах.

введите сюда описание изображения


Если же я буду использовать компонент без forwardRef, допустим таким способом, то я получу необходимые мне ошибки:

import { forwardRef, Ref } from "react";

export interface ICelebrationScreenProps
{
    title: string;
    [ prop: string ]: any;
}

function CelebrationScreen({ title, ...props }: ICelebrationScreenProps, ref: Ref<HTMLDivElement>)
{
    return (<>{title}</>);
};

export default forwardRef(CelebrationScreen);

function Test()
{
    return <CelebrationScreen />
}

Свойство "title" отсутствует в типе "{}" и является обязательным в типе "ICelebrationScreenProps". ts(2741)

Вы также можете увидеть это поведение на

Edit wonderful-rubin-lvnwpr


Ответы (3 шт):

Автор решения: meinefinsternis
// function forwardRef<T, P = {}>(render: ForwardRefRenderFunction<T, P>): 
// ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

const CelebrationScreen = forwardRef<HTMLDivElement, CelebrationScreenProps>(
({ title }, ref) => {
return (
    <div ref={ref}>{title}</div>
   );
  },
 );
→ Ссылка
Автор решения: Franz

Я думаю, что это происходит потому, что при использовании forwardRef компонент оборачивается в HOC (Higher-Order Component), который может изменять типы пропсов. В данном случае, тип пропсов компонента CelebrationScreen после обертки forwardRef становится неявным и VSCode не может определить его автоматически.

Чтобы решить эту проблему, можно явно указать тип пропсов при использовании компонента:

import { forwardRef, Ref } from "react";
import { ICelebrationScreenProps } from "./CelebrationScreen";

function Test()
{
    const ref = useRef<HTMLDivElement>(null);
    const props: ICelebrationScreenProps = {
        title: "My Title",
        myCustomProp: "Custom Value"
    };

    return <CelebrationScreen {...props} ref={ref} />;
}

В этом случае, VSCode будет иметь доступ к типу пропсов и сможет предоставлять подсказки и ошибки при работе с компонентом.

→ Ссылка
Автор решения: Daniil Loban

Для того чтобы наиболее гибко использовать типизацию можно завести определенный интерфейс ForwardRefExoticComponent (подробнее):

import { forwardRef, ReactElement, Ref, WeakValidationMap } from "react";

interface ForwardRefExoticComponent<P> {
  (props: P): ReactElement | null;
  readonly $$typeof: symbol;
  displayName?: string;
  defaultProps?: Partial<P>;
  propTypes?: WeakValidationMap<P>;
}

Сам интерфейс ForwardRefExoticComponent можно не прописывать, а импортировать из реакта.

import { ForwardRefExoticComponent } from "react";

Далее мы сможем использовать его подобным образом, типизируя под конкретные пропсы и получать подстветку ошибки в JSX:

const CelebrationScreenWithRef: ForwardRefExoticComponent<ICelebrationScreenProps> = forwardRef(
  CelebrationScreen
);
→ Ссылка