Основы типизации HOC
Начнём с чего-то простого: напишем HOC, который добавляет новые пропсы к обёрнутому компоненту. Например, HOC, который добавляет текущую дату.
import React from 'react';
// Интерфейс для дополнительных пропсов
interface WithDateProps {
currentDate: string;
}
// HOC, добавляющий `currentDate` в компонент
function withDate<P>(WrappedComponent: React.ComponentType<P & WithDateProps>) {
return (props: P) => {
const currentDate = new Date().toISOString();
return <WrappedComponent {...props} currentDate={currentDate} />;
};
}
// Пример использования
interface OriginalProps {
name: string;
}
const OriginalComponent: React.FC<OriginalProps & WithDateProps> = ({ name, currentDate }) => (
<p>
Привет, {name}! Сейчас {currentDate}.
</p>
);
const EnhancedComponent = withDate(OriginalComponent);
Что здесь происходит?
- Мы определили интерфейс
WithDatePropsдля пропсов, которые добавляет наш HOC. - HOC принимает компонент через параметр
WrappedComponent. - Мы добавили новые пропсы
currentDateи передали все оригинальные пропсы дальше через{...props}.
Типизация HOC
Обратите внимание на <P & WithDateProps>. Здесь:
P— это тип пропсов оригинального компонента.- Мы говорим, что обёрнутый компонент принимает как оригинальные пропсы, так и добавленные
WithDateProps.
Удаление лишних пропсов
Иногда HOC добавляет пропсы, которые не должны быть видимы для внешнего мира. Например, пропсы, которые используются только внутри HOC, не должны просачиваться наружу.
Давайте доработаем предыдущий пример:
function withDate<P>(WrappedComponent: React.ComponentTyp<P & WithDateProps>) {
return (props: P) => {
const currentDate = new Date().toISOString();
// Удаляем проброс лишних пропсов
return <WrappedComponent {...props} currentDate={currentDate} />;
};
}
// Тип обёрнутого компонента
type WrappedComponentType = typeof OriginalComponent;
// Оригинал и добавленные пропсы остаются строго типизированными
const EnhancedComponent = withDate(OriginalComponent);
В этом примере HOC не пробрасывает свои внутренние данные наружу, что делает интерфейс компонента более гибким.
Типизация HOC с пропсами: сложный случай
HOC может принимать свои собственные пропсы. Например, представьте, что мы делаем HOC, который добавляет условное отображение компонента только для авторизованных пользователей:
import React from 'react';
// Интерфейс для пропсов HOC
interface WithAuthProps {
isAuthenticated: boolean;
}
// HOC, добавляющий условное отображение
function withAuth<P>(WrappedComponent: React.ComponentType<P>) {
return ({ isAuthenticated, ...props }: P & WithAuthProps) => {
if (!isAuthenticated) {
return <p>Доступ запрещён</p>;
}
return <WrappedComponent {...props as P} />;
};
}
// Пример использования
interface UserProps {
username: string;
}
const UserComponent: React.FC<UserProps> = ({ username }) => (
<p>Добро пожаловать, {username}!</p>
);
const AuthenticatedUserComponent = withAuth(UserComponent);
Что этот код делает?
- HOC принимает пропсу
isAuthenticatedи решает, отображать ли компонент. - Мы используем оператор TypeScript
as P, чтобы явно указать, что остальные пропсы...propsсоответствуют типуP.
Примеры сложной типизации
Полиморфизм в HOC
Иногда нам нужны HOC, которые будут работать с разными компонентами и их типами пропсов. Это делается через обобщённые типы generics.
Например, HOC для логгирования пропсов:
function withLogger<P>(WrappedComponent: React.ComponentType<P>) {
return (props: P) => {
console.log('Пропсы:', props);
return <WrappedComponent {...props} />;
};
}
// Пример использования
const LoggedOriginalComponent = withLogger(OriginalComponent);
Этот HOC универсально работает с любым компонентом, независимо от его пропсов. Использование дженериков P обеспечивает гибкость и безопасность.
Разбор типичных ошибок и подходов
Проблема 1: неправильный порядок пропсов
Иногда при типизации HOC вы можете забыть пересечение типов P & ExtraProps и просто указать P. Это может привести к ошибкам из-за отсутствия новых пропсов у обёрнутого компонента.
Правильно:
React.ComponentType<P & ExtraProps>.
Неправильно:
React.ComponentType<P>.
Проблема 2: типы пропсов теряются
Если HOC не передаёт оригинальные пропсы, это может вызвать неожиданные ошибки. Убедитесь, что вы занимаете только те пропсы, которые действительно нужны вашему HOC, и не забывайте пробрасывать остальные через {...props}.
Проблема 3: сложность при сильной вложенности
При работе с несколькими HOC типизация может стать сложной. Используйте React.ComponentProps<typeof YourComponent> для автоматического извлечения типа пропсов из существующего компонента.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ