Сделайте ContainerFc generic-функцией, выводящей props из переданного Component. NoInfer не позволит другим входным props изменить этот выведенный контракт.
Корректно типизировать передаваемый в контейнер компонент
Сделайте ContainerFc generic-функцией, выводящей props из переданного Component. NoInfer не позволит другим входным props изменить этот выведенный контракт.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
При использовании компонента OtherFC в качестве значения свойства Component у ContainerFc TypeScript должен подсвечивать и предлагать остальные свойства, которые можно передать в ContainerFc помимо его собственных, с точным указанием их типов.
import { FC } from "react";
type OtherProps = {
name: string;
age: number;
};
const OtherFC: FC<OtherProps> = ({ age, name }) => {
return (
<div>
{name} {age}
</div>
);
};
/** Начало блока, где разрешено менять и дополнять типы */
type IProps = {
Component: any;
height: number;
};
const ContainerFc: FC<IProps> = (props) => {
const { Component, height } = props;
return (
<div style={{ height: `${height}px` }}>
<Component {...props} />
</div>
);
};
/** Конец блока, где можно редактировать типы */
/** Здесь типовые ошибки не должны возникать */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const AnyComponent1 = () => {
return <ContainerFc height={5} Component={OtherFC} name="Макс" age={30} />;
};
/** Здесь должны появляться ошибки типизации */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const AnyComponent2 = () => {
return <ContainerFc height={5} Component={OtherFC} name={30} age="Макс" />;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const AnyComponent3 = () => {
return <ContainerFc height={5} Component={OtherFC} role="Макс" />;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const AnyComponent4 = () => {
return <ContainerFc height={5} Component={OtherFC} name="Макс" />;
};
Ответ
Сделайте ContainerFc generic-функцией, выводящей props из переданного Component. NoInfer не позволит другим входным props изменить этот выведенный контракт.
В TypeScript 5.4+:
type IProps<P extends object> = {
Component: React.ComponentType<P>;
height: number;
} & NoInfer<P>;
function ContainerFc<P extends object>(props: IProps<P>) {
const { Component, height, ...rest } = props;
return <div style={{ height: `${height}px` }}>
<Component {...(rest as P)} />
</div>;
}
Name/age выводятся из OtherFC, поэтому неверные типы, лишний role и отсутствие age отвергаются. Height и Component зарезервированы контейнером: решение предполагает отсутствие одноимённых обязательных props у вложенного компонента. Rest assertion здесь лишь обходит ограничение вывода при generic-деструктуризации.
Источник: www.typescriptlang.org.